小程序生命周期
運(yùn)行機(jī)制
小程序什么時(shí)候會被銷毀
當(dāng)小程序進(jìn)入后臺茂装,客戶端會維持一段時(shí)間的運(yùn)行狀態(tài)宁否,超過一定時(shí)間后(目前是5分鐘)會被微信主動(dòng)銷毀副渴。
當(dāng)短時(shí)間內(nèi)(5s)連續(xù)收到兩次以上收到系統(tǒng)內(nèi)存告警翼悴,會進(jìn)行小程序的銷毀信卡。
再次打開邏輯
用戶打開小程序的預(yù)期有以下兩類場景:
- A. 打開首頁:場景值有1001隔缀,1019,1022傍菇,1023蚕泽,1038,1056
- B. 打開小程序指定的某個(gè)頁面:場景值為除A以外的其他
當(dāng)再次打開一個(gè)小程序邏輯如下:
上一次的場景 | 當(dāng)前打開的場景 | 效果 |
---|---|---|
A | A | 保留原來的狀態(tài) |
B | A | 清空原來的頁面棧桥嗤,打開首頁(相當(dāng)于執(zhí)行wx.reLaunch到首頁) |
A或B | B | 清空原來的頁面棧须妻,打開指定頁面(相當(dāng)于執(zhí)行wx.reLaunch到指定頁 |
小程序的生命周期
App()函數(shù)注冊一個(gè)小程序。接受一個(gè)Object參數(shù)泛领,其指定小程序的生命周期回調(diào)等荒吏。這里的生命周期針對整個(gè)小程序項(xiàng)目,而不是哪個(gè)頁面渊鞋。
object參數(shù)說明:
前臺绰更、后臺定義: 當(dāng)用戶點(diǎn)擊左上角關(guān)閉,或者按了設(shè)備Home鍵離開微信锡宋,小程序并沒有直接銷毀儡湾,而是進(jìn)入了后臺;當(dāng)在此進(jìn)入微信或再次打開小程序执俩,又會從后臺進(jìn)入前臺徐钠。
下面是一個(gè)示例,代碼如下:
//app.js
App({
onLaunch: function (options) {
// 小程序初始化完成時(shí)觸發(fā)役首,全局只觸發(fā)一次尝丐。
// options說明:
// path:打開小程序的路徑
// query 打開小程序的query
// scene 打開小程序的場景值
// shareTicket 轉(zhuǎn)發(fā)信息相關(guān)
// referrerInfo 當(dāng)場景為由另一個(gè)小程序或公眾號或App打開時(shí),返回此字段
console.log('app >> onLaunch 衡奥, options::',options);
},
onShow: function(options){
// 小程序啟動(dòng)爹袁,或從后臺進(jìn)入前臺顯示時(shí)觸發(fā)
// 參數(shù)與onLaunch一致
console.log('app >> onShow值依, options :: ',options);
},
onHide:function(){
//小程序從前臺進(jìn)入后臺時(shí)觸發(fā)晾剖。
console.log('app >> onHide');
},
onError:function(error){
// 小程序發(fā)生腳本錯(cuò)誤顽腾,或者api調(diào)用失敗時(shí)觸發(fā)蛾绎。
// error String 錯(cuò)誤信息缤弦,包含堆棧信息
console.log('app >> onError 年叮, error::'+error);
},
onPageNotFound(Object){
// 基礎(chǔ)庫1.9.90開始支持
// 小程序要打開的頁面不存在時(shí)觸發(fā)砂轻。
// Object參數(shù)說明:
// path String 不存在的頁面路徑
// query object 打開不存在頁面的query
// isEntryPage Boolean 是否本次啟動(dòng)的首個(gè)頁面
console.log('app >> onPageNotFound , Object :: ',Object);
//注:如果開發(fā)者沒有添加 onPageNotFound 監(jiān)聽胃珍,當(dāng)跳轉(zhuǎn)頁面不存在時(shí)辰晕,將推入微信客戶端原生的頁面不存在提示頁面蛤迎。
// 如果 onPageNotFound 回調(diào)中又重定向到另一個(gè)不存在的頁面,將推入微信客戶端原生的頁面不存在提示頁面含友,并且不再回調(diào) onPageNotFound替裆。
}
})
當(dāng)啟動(dòng)小程序時(shí)校辩,在開發(fā)者工具中可以看到控制臺打印如下信息
app
頁面的生命周期
Page(Object)函數(shù)用來注冊一個(gè)頁面。接受一個(gè)Object類型參數(shù)辆童,其指定頁面的初始數(shù)據(jù)宜咒、生命周期回調(diào)、時(shí)間處理函數(shù)等把鉴。
下面用一個(gè)示例來嘗試下小程序的生命周期流程
該示例有4個(gè)頁面pageA/pageB/pageC/pageD故黑,其中pageA和pageB是兩個(gè)tab頁面,即通過底部標(biāo)簽切換的頁面庭砍。而pageC和pageD是兩個(gè)普通頁面场晶。示例頁面如下:
pageA
pageB
pageC
pageD
四個(gè)頁面如圖所示,有一些按鈕分別表示不同的路由跳轉(zhuǎn)方式怠缸。
下面是pageA相關(guān)的部分頁面代碼诗轻,其他頁面類似
Page({
data:{
},
onLoad:function(options){
//頁面加載時(shí)觸發(fā)。一個(gè)頁面只會調(diào)用一次揭北,可以在onLoad的參數(shù)中獲取打開當(dāng)前路徑中的參數(shù)扳炬。
//參數(shù) options Object 打開當(dāng)前頁面路徑中的參數(shù)
console.log('pageA >> onLoad , options ::',options);
},
onReady:function(){
//頁面初次渲染完成時(shí)觸發(fā)。一個(gè)頁面只會調(diào)用一次搔体,代表頁面已經(jīng)準(zhǔn)備妥當(dāng)恨樟,可以和視圖層進(jìn)行交互。
console.log('pageA >> onReady');
},
onShow:function(){
//頁面顯示/切入前臺時(shí)觸發(fā)
console.log('pageA >> onShow');
},
onHide:function(){
//頁面隱藏/切入后臺時(shí)觸發(fā)疚俱。
console.log('pageA >> onHide');
},
onUnload:function(){
//頁面卸載時(shí)觸發(fā)劝术。
console.log('pageA >> onUnload');
},
// 自定義方法
navigateToC:function(){
//保留當(dāng)前頁面,跳轉(zhuǎn)到應(yīng)用內(nèi)的某個(gè)頁面计螺,但是不能跳到 tabbar 頁面夯尽。使用 wx.navigateBack 可以返回到原頁面。
console.log('%cpageA==========navigateToC===========','color:red');
wx.navigateTo({
url:'/pages/page-c/index'
});
},
redirectToC:function(){
//關(guān)閉當(dāng)前頁面登馒,跳轉(zhuǎn)到應(yīng)用內(nèi)的某個(gè)頁面,但是不允許跳轉(zhuǎn)到tabbar頁面
console.log('%cpageA==========redirectToC===========','color:red');
wx.redirectTo({
url:'/pages/page-c/index'
});
},
reLaunchToC:function(){
//關(guān)閉所有頁面咆槽,打開到應(yīng)用內(nèi)的某個(gè)頁面
console.log('%cpageA==========reLaunchToC===========','color:red');
wx.reLaunch({
url:'/pages/page-c/index'
})
}
});
下面是各種情況下的試驗(yàn)結(jié)果:
當(dāng)前頁面 | 路由后頁面 | 跳轉(zhuǎn)方式 | 觸發(fā)的生命周期(按順序) | 說明 |
---|---|---|---|---|
A | A | 首次打開 | first
|
執(zhí)行小程序的onlaunch>onShow 陈轿,然后執(zhí)行頁面的onLoad>onShow>onReady
|
A | B | 點(diǎn)擊tab標(biāo)簽 | AtoB
|
pageA隱藏,pageB加載 |
A | B(再次打開) | 點(diǎn)擊tab標(biāo)簽 | 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 | 關(guān)閉小程序 | image.png
|
打開A/B/C/D峻呛,從pageD關(guān)閉小程序罗售,可以看到執(zhí)行的D和App的onHide ,小程序并沒有真正退出 |
上面說到的情況都較為簡單的流程钩述,從官方文檔便可以理解到寨躁,下面試驗(yàn)一些復(fù)雜的流程。
第一種
A->C->D-B
牙勘,其實(shí)這個(gè)過程按正常思維便可以理解职恳,下圖為整個(gè)過程的展示:
ACDB
上面的圖片展示了小程序從打開到走完這個(gè)流程的所有生命周期,其余都好理解方面,值得注意的是放钦,DswitchTabTo
B的時(shí)候,會干掉所有其他非tabBar頁面恭金,所以pageC和pageD都會觸發(fā)onUnload
第二種
A->C->C->C
最筒,這種情況下我們從C頁面繼續(xù)跳轉(zhuǎn)到C頁面,為了看下小程序是新創(chuàng)建一個(gè)C頁面蔚叨,還是復(fù)用之前的C頁面床蜘,我們在C頁面中加一個(gè)input用來識別頁面是否被復(fù)用。試驗(yàn)結(jié)果如下圖:
ACCC
事實(shí)證明每次我們都打開了一個(gè)新的頁面蔑水,我們的輸入框是空白的邢锯,而我每次分別輸入了1/2/3,從生命周期函數(shù)也可以看到每次C都執(zhí)行了onHide但沒有執(zhí)行onUnload
說明之前的頁面還在搀别,而每次打開C都執(zhí)行了onLoad/onShow/onReady
說明我們打開的是一個(gè)新的頁面丹擎,但是打開控制臺看頁面棧信息,截圖如下:
trace
頁面棧樹中只有A和C兩個(gè)歇父,不過我們跳轉(zhuǎn)時(shí)打開這里可以看到C的__webviewId__
是在變化的蒂培,也證明了我們打開的是一個(gè)新的頁面。雖然從控制臺AppData的Tree看上去有要兩個(gè)榜苫,實(shí)際上我們跳轉(zhuǎn)夠10次還是會受到微信頁面深度最大為10層的限制护戳。下面按微信左上角的返回鍵,看下生命周期流程:
Cback
可以看到依次執(zhí)行了C頁面的onUnload/onShow
垂睬,也就是之前的多個(gè)C頁面被一一卸載掉了媳荒。這里注意一點(diǎn),對于二級頁面驹饺,使用navigateBake或者微信自己的返回按鍵都會卸載掉當(dāng)前頁面钳枕,所以離開頁面只有navigateTo的時(shí)候會保留當(dāng)前頁面,其他情況都會卸載掉當(dāng)前頁面赏壹。(自我總結(jié):對于二級頁面鱼炒,只有從下一個(gè)頁面返回自身的情況下,不調(diào)用onLoad其他任何情況進(jìn)入二級頁面蝌借,都會觸發(fā)onLoad昔瞧。沒有想到其他情況指蚁,顧作此總結(jié),歡迎指正)硬爆。
組件的生命周期函數(shù)
小程序支持自定義組件欣舵,使用Component構(gòu)造器定義組件,使用Component構(gòu)造器時(shí)可以定義組件的屬性缀磕、數(shù)據(jù)缘圈、方法等。這里整理下生命周期相關(guān)函數(shù)袜蚕。
組件的生命周期函數(shù)有兩種形式糟把,除了寫在外面,還可以統(tǒng)一寫在lifetimes
中牲剃,在下面的示例代碼備注中可以看到遣疯。
Component({
properties:{
innerText:{
type:String
}
},
data:{
},
methods:{
},
created:function(){
// 組件生命周期函數(shù),在組件實(shí)例進(jìn)入頁面節(jié)點(diǎn)樹時(shí)執(zhí)行凿傅,注意此時(shí)不能調(diào)用setData
console.log('Component-1 >> created');
},
attached:function(){
// 組件生命周期函數(shù)缠犀,在組件實(shí)例進(jìn)入頁面節(jié)點(diǎn)樹時(shí)執(zhí)行。
console.log('Component-1 >> attached');
},
ready:function(){
// 在組件布局完成后執(zhí)行聪舒,此時(shí)可以獲取節(jié)點(diǎn)信息
console.log('Component-1 >> ready');
},
moved:function(){
// 在組件實(shí)例被移動(dòng)到節(jié)點(diǎn)樹另一個(gè)位置時(shí)執(zhí)行
console.log('Component-1 >> moved');
},
detached:function(){
// 在組件實(shí)例被從頁面節(jié)點(diǎn)樹移除時(shí)執(zhí)行
console.log('Component-1 >> detached');
},
lifetimes:{
// 組件生命周期聲明對象辨液,將組件的生命周期收歸到該字段進(jìn)行聲明,原有聲明方式仍舊有效箱残,如同時(shí)存在兩種聲明方式滔迈,則lifetimes字段內(nèi)聲明方式優(yōu)先級最高
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兩個(gè)生命周期
show:function(){
console.log('Component-1 pageLifetimes >> Show');
},
hide:function(){
console.log('Component-1 pageLifetimes >> Hide');
}
}
})
分別在B頁面和C頁面引入該組件被辑, 從以下幾種情況看下生命周期函數(shù)的執(zhí)行過程
第一種情況同時(shí)引入上面所有生命周期函數(shù)燎悍,由A通過tab切換到B,再由B通過navigateTo切換到C盼理,生命周期執(zhí)行打印如下:
Components
可以看到組件中只執(zhí)行了lifetimes
中的生命周期函數(shù)谈山,外層的生命周期函數(shù)并沒有執(zhí)行。而且可以看到先執(zhí)行組件的created/attached
函數(shù)榜揖,隨后執(zhí)行頁面的onLoad/onShow
勾哩,再執(zhí)行組件的ready
,最后執(zhí)行頁面的onReady
举哟,這是頁面中引入組件時(shí)組件的生命周期函數(shù)執(zhí)行順序。
lifetimes中的生命周期函數(shù)執(zhí)行了迅矛,外層的生命周期函數(shù)沒有執(zhí)行妨猩,所有當(dāng)兩者同時(shí)存在時(shí),lifetimes中的優(yōu)先級要高秽褒。
這里組件中的pageLifetimes
沒有執(zhí)行壶硅,不清楚具體原因威兜,官網(wǎng)說是2.2.3版本以上支持,我在2.3.0的環(huán)境還是沒有執(zhí)行庐椒,不清楚具體原因椒舵,求解!
第二種情況约谈,不引入lifetimes的生命周期函數(shù)笔宿,只使用外層的生命周期函數(shù),執(zhí)行結(jié)果如下圖所示:
COMPONENT
可以看到棱诱,生命周期函數(shù)執(zhí)行順序沒有變泼橘,外層的生命周期生效。
第三種情況迈勋,在B頁面中使用兩個(gè)組件炬灭,這里我把lifetimes中的created生命周期注釋掉了,看生命周期的執(zhí)行情況靡菇,這里組件1和組件2的代碼相同重归,執(zhí)行結(jié)果情況如下圖:
image.png
從執(zhí)行的結(jié)果來看,整個(gè)生命周期的執(zhí)行順序不變厦凤,只是要在每個(gè)階段執(zhí)行所有組件的相應(yīng)生命周期鼻吮,如上圖,現(xiàn)行玩所有組件的created泳唠,再執(zhí)行所有組件的attached,然后執(zhí)行頁面的onLoad和onShow狈网,再執(zhí)行所有組件的ready,最后執(zhí)行頁面的onReady笨腥。
總結(jié):通過這些試驗(yàn)拓哺,對小程序相關(guān)的生命周期有了一個(gè)基本的認(rèn)識。
1脖母、小程序初次打開會執(zhí)行小程序的生命周期鉤子函數(shù):onLaunch->onShow
士鸥,而且這些鉤子函數(shù)只會執(zhí)行一次。關(guān)閉小程序谆级,小程序并不會真正退出烤礁,所以執(zhí)只行了onHide
。
2肥照、頁面的初次打開會執(zhí)行頁面的生命周期鉤子函數(shù):onLoad->onShow->onReady
脚仔,通過navigateTo
離開頁面會保留該頁面,此時(shí)只執(zhí)行onHide
舆绎,其他方式離開(包括navigateBack)都會干掉當(dāng)前頁面鲤脏,此時(shí)會執(zhí)行onHide>onUnload
。特殊情況:switchTabTo
會干掉所有非tab頁面,但是保留所有已經(jīng)加載的tab頁面猎醇。
3窥突、包含組件的頁面,先執(zhí)行所有組件的created
硫嘶,再執(zhí)行所有組件的attached
阻问,然后執(zhí)行頁面的onLoad>onshow
,再執(zhí)行所有組件的ready
沦疾,隨后執(zhí)行頁面的onReady
称近。當(dāng)頁面被卸載時(shí),先執(zhí)行頁面的onUnload
曹鸠,再執(zhí)行組件的detached
煌茬。頁面不卸載,不會觸發(fā)組件的detached
彻桃。
初次接觸小程序坛善,能力有限,歡迎指正邻眷![/抱拳]
參考文檔:https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/component.html