微信小程序生命周期淺析

小程序生命周期

運行機制

小程序什么時候會被銷毀
當小程序進入后臺,客戶端會維持一段時間的運行狀態,超過一定時間后(目前是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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容