小程序生命周期浅析

作者: 皮皮坤666 | 来源:发表于2018-09-28 12:34 被阅读14次

    小程序生命周期

    运行机制

    小程序什么时候会被销毁
    当小程序进入后台,客户端会维持一段时间的运行状态,超过一定时间后(目前是5分钟)会被微信主动销毁。
    当短时间内(5s)连续收到两次以上收到系统内存告警,会进行小程序的销毁。
    再次打开逻辑
    用户打开小程序的预期有以下两类场景:
    • A. 打开首页:场景值有1001,1019,1022,1023,1038,1056
    • B. 打开小程序指定的某个页面:场景值为除A以外的其他

    当再次打开一个小程序逻辑如下:

    上一次的场景 当前打开的场景 效果
    A A 保留原来的状态
    B A 清空原来的页面栈,打开首页(相当于执行wx.reLaunch到首页)
    A或B B 清空原来的页面栈,打开指定页面(相当于执行wx.reLaunch到指定页

    小程序的生命周期

    App()函数注册一个小程序。接受一个Object参数,其指定小程序的生命周期回调等。这里的生命周期针对整个小程序项目,而不是哪个页面。
    object参数说明:
    前台、后台定义: 当用户点击左上角关闭,或者按了设备Home键离开微信,小程序并没有直接销毁,而是进入了后台;当在此进入微信或再次打开小程序,又会从后台进入前台。
    下面是一个示例,代码如下:
        //app.js
        App({
          onLaunch: function (options) {
            // 小程序初始化完成时触发,全局只触发一次。
            // options说明:
            // path:打开小程序的路径
            // query 打开小程序的query
            // scene 打开小程序的场景值
            // shareTicket 转发信息相关
            // referrerInfo 当场景为由另一个小程序或公众号或App打开时,返回此字段
             console.log('app >> onLaunch , options::',options);
          },
          onShow: function(options){
            // 小程序启动,或从后台进入前台显示时触发
            // 参数与onLaunch一致
            console.log('app >> onShow, options :: ',options);
          },
          onHide:function(){
            //小程序从前台进入后台时触发。
            console.log('app >> onHide');
          },
          onError:function(error){
            // 小程序发生脚本错误,或者api调用失败时触发。
            // error String 错误信息,包含堆栈信息
            console.log('app >> onError , error::'+error);
          },
          onPageNotFound(Object){
            // 基础库1.9.90开始支持
            // 小程序要打开的页面不存在时触发。
            // Object参数说明:
            // path String 不存在的页面路径
            // query object 打开不存在页面的query
            // isEntryPage Boolean 是否本次启动的首个页面
            console.log('app >> onPageNotFound , Object :: ',Object);
            //注:如果开发者没有添加 onPageNotFound 监听,当跳转页面不存在时,将推入微信客户端原生的页面不存在提示页面。
            // 如果 onPageNotFound 回调中又重定向到另一个不存在的页面,将推入微信客户端原生的页面不存在提示页面,并且不再回调 onPageNotFound。
          }
        })
    
    当启动小程序时,在开发者工具中可以看到控制台打印如下信息
    app

    页面的生命周期

    Page(Object)函数用来注册一个页面。接受一个Object类型参数,其指定页面的初始数据、生命周期回调、时间处理函数等。

    下面用一个示例来尝试下小程序的生命周期流程

    该示例有4个页面pageA/pageB/pageC/pageD,其中pageA和pageB是两个tab页面,即通过底部标签切换的页面。而pageC和pageD是两个普通页面。示例页面如下:
    pageA pageB pageC pageD
    四个页面如图所示,有一些按钮分别表示不同的路由跳转方式。
    下面是pageA相关的部分页面代码,其他页面类似
        Page({
            data:{
    
            },
            onLoad:function(options){
                //页面加载时触发。一个页面只会调用一次,可以在onLoad的参数中获取打开当前路径中的参数。
                //参数 options Object 打开当前页面路径中的参数
                console.log('pageA >> onLoad , options ::',options);
            },
            onReady:function(){
                //页面初次渲染完成时触发。一个页面只会调用一次,代表页面已经准备妥当,可以和视图层进行交互。
                console.log('pageA >> onReady');
            },
            onShow:function(){
                //页面显示/切入前台时触发
                console.log('pageA >> onShow');
            },
            onHide:function(){
                //页面隐藏/切入后台时触发。
                console.log('pageA >> onHide');
            },
            onUnload:function(){
                //页面卸载时触发。
                console.log('pageA >> onUnload');
            },
            // 自定义方法
            navigateToC:function(){
                //保留当前页面,跳转到应用内的某个页面,但是不能跳到 tabbar 页面。使用 wx.navigateBack 可以返回到原页面。
                console.log('%cpageA==========navigateToC===========','color:red');
                wx.navigateTo({
                    url:'/pages/page-c/index'
                });
            },
            redirectToC:function(){
                //关闭当前页面,跳转到应用内的某个页面,但是不允许跳转到tabbar页面
                console.log('%cpageA==========redirectToC===========','color:red');
                wx.redirectTo({
                    url:'/pages/page-c/index'
                });
            },
            reLaunchToC:function(){
                //关闭所有页面,打开到应用内的某个页面
                console.log('%cpageA==========reLaunchToC===========','color:red');
                wx.reLaunch({
                    url:'/pages/page-c/index'
                })
            }
        });
    
    
    

    下面是各种情况下的试验结果:

    当前页面 路由后页面 跳转方式 触发的生命周期(按顺序) 说明
    A A 首次打开 first 执行小程序的onlaunch>onShow,然后执行页面的onLoad>onShow>onReady
    A B 点击tab标签 AtoB pageA隐藏,pageB加载
    A B(再次打开) 点击tab标签 AtoB2 pageA隐藏,pageB显示
    A C navigateTo An2C pageA隐藏,pageC加载
    A C redirectTo Ar2C pageA卸载,pageC加载
    A C reLaunchTo AL2c 卸载所有页面,pageC加载
    C A switchTabTo Cs2A 卸载所有非tab页面,pageA显示
    D C navigateBack Db2C pageD卸载,pageC显示
    D B switchTabTo Ds2B 卸载所有非tab页面,pageC显示,pageA任然存在
    D 关闭小程序 image.png 打开A/B/C/D,从pageD关闭小程序,可以看到执行的D和App的onHide,小程序并没有真正退出
    上面说到的情况都较为简单的流程,从官方文档便可以理解到,下面试验一些复杂的流程。
    第一种 A->C->D-B,其实这个过程按正常思维便可以理解,下图为整个过程的展示:
    ACDB
    上面的图片展示了小程序从打开到走完这个流程的所有生命周期,其余都好理解,值得注意的是,DswitchTabToB的时候,会干掉所有其他非tabBar页面,所以pageC和pageD都会触发onUnload
    第二种 A->C->C->C,这种情况下我们从C页面继续跳转到C页面,为了看下小程序是新创建一个C页面,还是复用之前的C页面,我们在C页面中加一个input用来识别页面是否被复用。试验结果如下图:
    ACCC
    事实证明每次我们都打开了一个新的页面,我们的输入框是空白的,而我每次分别输入了1/2/3,从生命周期函数也可以看到每次C都执行了onHide但没有执行onUnload说明之前的页面还在,而每次打开C都执行了onLoad/onShow/onReady说明我们打开的是一个新的页面,但是打开控制台看页面栈信息,截图如下:
    trace
    页面栈树中只有A和C两个,不过我们跳转时打开这里可以看到C的__webviewId__是在变化的,也证明了我们打开的是一个新的页面。虽然从控制台AppData的Tree看上去有要两个,实际上我们跳转够10次还是会受到微信页面深度最大为10层的限制。下面按微信左上角的返回键,看下生命周期流程:
    Cback
    可以看到依次执行了C页面的onUnload/onShow,也就是之前的多个C页面被一一卸载掉了。这里注意一点,对于二级页面,使用navigateBake或者微信自己的返回按键都会卸载掉当前页面,所以离开页面只有navigateTo的时候会保留当前页面,其他情况都会卸载掉当前页面。(自我总结:对于二级页面,只有从下一个页面返回自身的情况下,不调用onLoad其他任何情况进入二级页面,都会触发onLoad。没有想到其他情况,顾作此总结,欢迎指正)。

    组件的生命周期函数

    小程序支持自定义组件,使用Component构造器定义组件,使用Component构造器时可以定义组件的属性、数据、方法等。这里整理下生命周期相关函数。
    组件的生命周期函数有两种形式,除了写在外面,还可以统一写在lifetimes中,在下面的示例代码备注中可以看到。
        Component({
            properties:{
                innerText:{
                    type:String
                }
            },
            data:{
    
            },
            methods:{
    
            },
            created:function(){
                // 组件生命周期函数,在组件实例进入页面节点树时执行,注意此时不能调用setData
                console.log('Component-1 >> created');
            },
            attached:function(){
                // 组件生命周期函数,在组件实例进入页面节点树时执行。
                console.log('Component-1 >> attached');
            },
            ready:function(){
                // 在组件布局完成后执行,此时可以获取节点信息
                console.log('Component-1 >> ready');
            },
            moved:function(){
                // 在组件实例被移动到节点树另一个位置时执行
                console.log('Component-1 >> moved');
            },
            detached:function(){
                // 在组件实例被从页面节点树移除时执行
                console.log('Component-1 >> detached');
            },
            lifetimes:{
                // 组件生命周期声明对象,将组件的生命周期收归到该字段进行声明,原有声明方式仍旧有效,如同时存在两种声明方式,则lifetimes字段内声明方式优先级最高
                created:function(){
                    console.log('Component-1 lifetimes >> created');
                },
                attached:function(){
                    console.log('Component-1 lifetimes >> attached');
                },
                ready:function(){
                    console.log('Component-1 lifetimes >> ready');
                },
                moved:function(){
                    console.log('Component-1 lifetimes >> moved');
                },
                detached:function(){
                    console.log('Component-1 lifetimes >> detached');
                }
            },
            pageLifetimes:{
                // 组件所在页面的生命周期声明对象,目前仅支持页面的show和hide两个生命周期
                show:function(){
                    console.log('Component-1 pageLifetimes >> Show');
                },
                hide:function(){
                    console.log('Component-1 pageLifetimes >> Hide');
                }
            }
    
        })
    

    分别在B页面和C页面引入该组件, 从以下几种情况看下生命周期函数的执行过程

    第一种情况同时引入上面所有生命周期函数,由A通过tab切换到B,再由B通过navigateTo切换到C,生命周期执行打印如下:

    Components
    可以看到组件中只执行了lifetimes中的生命周期函数,外层的生命周期函数并没有执行。而且可以看到先执行组件的created/attached函数,随后执行页面的onLoad/onShow,再执行之间的ready,最后执行页面的的onReady,这是页面中引入组件时组件的生命周期函数执行顺序。
    lifetimes中的生命周期函数执行了,外层的生命周期函数没有执行,所有当两者同时存在时,lifetimes中的优先级要高。
    这里组件中的pageLifetimes没有执行,不清楚具体原因,官网说是2.2.3版本以上支持,我在2.3.0的环境还是没有执行,不清楚具体原因,求解!

    第二种情况,不引入lifetimes的生命周期函数,只使用外层的生命周期函数,执行结果如下图所示:

    COMPONENT
    可以看到,生命周期函数执行顺序没有变,外层的生命周期生效。

    第三种情况,在B页面中使用两个组件,这里我把lifetimes中的created生命周期注释掉了,看生命周期的执行情况,这里组件1和组件2的代码相同,执行结果情况如下图:

    image.png
    从执行的结果来看,整个生命周期的执行顺序不变,只是要在每个阶段执行所有组件的相应生命周期,如上图,现行玩所有组件的created,再执行所有组件的attached,然后执行页面的onLoad和onShow,再执行所有组件的ready,最后执行页面的onReady。

    总结:通过这些试验,对小程序相关的生命周期有了一个基本的认识。

    1、小程序初次打开会执行小程序的生命周期钩子函数:onLaunch->onShow,而且这些钩子函数只会执行一次。关闭小程序,小程序并不会真正退出,所以执只行了onHide
    2、页面的初次打开会执行页面的生命周期钩子函数:onLoad->onShow->onReady,通过navigateTo离开页面会保留该页面,此时只执行onHide,其他方式离开(包括navigateBack)都会干掉当前页面,此时会执行onHide>onUnload。特殊情况:switchTabTo会干掉所有非tab页面,但是保留所有已经加载的tab页面。
    3、包含组件的页面,先执行所有组件的created,再执行所有组件的attached,然后执行页面的onLoad>onshow,再执行所有组件的ready,随后执行页面的onReady。当页面被卸载时,先执行页面的onUnload,再执行组件的detached。页面不卸载,不会触发组件的detached

    初次接触小程序,能力有限,欢迎指正![/抱拳]

    参考文档:https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/component.html

    相关文章

      网友评论

      本文标题:小程序生命周期浅析

      本文链接:https://www.haomeiwen.com/subject/sgzeoftx.html