小程序生命周期
運行機制
小程序什么時候會被銷毀
當小程序進入后臺,客戶端會維持一段時間的運行狀態,超過一定時間后(目前是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
上面的圖片展示了小程序從打開到走完這個流程的所有生命周期,其余都好理解,值得注意的是,DswitchTabTo
B的時候,會干掉所有其他非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