前言
以小程序爲切入點,深刻理解總結方方面面的知識點,作成系列文章,但願能獲得大神的指點和幫助新人入門,承上啓下才是好程序猿
因爲是系列第一篇文章,緊跟着的是一大段廢話,只關心技術的能夠跳過
- 轉眼半年又要過去了,意味着來新公司快半年了,離上次寫文章也半年了,渾渾噩噩得當碼農半年了,這半年主要是搬磚(定製VUE和小程序)各類重複作功,說實話沒太大長進,核心功能公司模板有了,大bug改不動,小bug改不完,大差不差交工就行,固然這是我本身的選擇,本身XXX含着淚也要XX,自行腦補
- 你們應該都知道有一種公司叫作外包公司,利弊網上一大堆,關鍵看你怎麼選擇了
- 我如今就在外包公司,當初的打算就是用工做量來提升熟練度,也確實達成了「目標」,半年前,對於vue和小程序,也就是mvvm,只是理論層面,實際作項目仍是有點怵,如今嘛結合上面的吐槽,前面的目標只能說達成了50%,畢竟作定製無論黑貓白貓,抓到老鼠就是好貓,遇到問題也是見招拆招,性能?迭代?可複用性?冗餘?這些和我有關係嗎?
- 針對上面這種狀況,也就出現了不少人生導師的吶喊——待在外包不能超過半年,否則你就廢了!!!(拿來鞭策本身)
- 如今咱們公司已經準備向產品轉型了,我也從定製部門轉到了小程序產品部門,正好深刻理解一波,和公司共同成長
- 感受知識也是金字塔狀的,剛開始咱們在頂端,越往下拓展的就越多,我發現每當我想深刻理解一個方面,必然引出更多關聯知識,那怎麼辦呢,好記性不如爛筆頭,在這年底年初之際,先給2019年開個好頭
小程序App生命週期
- 小程序App生命週期是在app.js裏面調用的,App(Object)函數用來註冊一個小程序,接受一個 Object 參數,指定其小程序的生命週期回調
- App() 必須在 app.js 中調用,必須調用且只能調用一次,否則會出現沒法預期的後果
以上應該一眼就能看明白,如下主要講講前臺、後臺定義和運行機制等
1 . 前臺、後臺定義javascript
- 當用戶點擊左上角關閉,或者按了設備 Home 鍵離開微信,小程序並無直接銷燬,而是進入了後臺onHide;當再次進入微信或再次打開小程序,又會從後臺進入前臺onShow。須要注意的是:只有當小程序進入後臺必定時間,或者系統資源佔用太高,纔會被真正的銷燬。
2 . 運行機制html
- 小程序啓動會有兩種狀況,一種是「冷啓動」,一種是「熱啓動」。 假如用戶已經打開過某小程序,而後在必定時間內再次打開該小程序,此時無需從新啓動,只需將後臺態的小程序切換到前臺onShow,這個過程就是熱啓動;冷啓動指的是用戶首次打開或小程序被微信主動銷燬後再次打開的狀況,此時小程序須要從新加載啓動onLauch
- 小程序沒有重啓的概念
- 當小程序進入後臺,客戶端會維持一段時間的運行狀態,超過必定時間後(目前是5分鐘)會被微信主動銷燬
- 在 iOS 上,當微信客戶端在必定時間間隔內(目前是 5 秒)連續收到兩次及以上系統內存告警時,會主動進行小程序的銷燬,並提示用戶 「該小程序可能致使微信響應變慢被終止」。建議小程序在必要時使用 wx.onMemoryWarning 監聽內存告警事件,進行必要的內存清理。
3 . 更新機制vue
- 小程序冷啓動時若是發現有新版本,將會異步下載新版本的代碼包,並同時用客戶端本地的包進行啓動,即新版本的小程序須要等下一次冷啓動纔會應用上。 若是須要立刻應用最新版本,可使用 wx.getUpdateManager API 進行處理
- 小程序強制更新
//注意,小程序的更新的api須要基礎庫在1.9.90以上
const updateManager = wx.getUpdateManager()
updateManager.onCheckForUpdate(function (res) {
// 請求完新版本信息的回調,省略回調會報錯,以下圖
console.log(res.hasUpdate)
})
updateManager.onUpdateReady(function () {
wx.showModal({
title: '更新提示',
content: '新版本已經準備好,是否重啓應用?',
success: function (res) {
if (res.confirm) {
// 新的版本已經下載好,調用 applyUpdate 應用新版本並重啓
updateManager.applyUpdate()
}
}
})
})
updateManager.onUpdateFailed(function () {
// 新的版本下載失敗
wx.showModal({
title: '更新提示',
content: '新版本下載失敗',
showCancel:false
})
})
3 . 再次打開邏輯java
- 用戶打開小程序的預期有如下兩類場景:一、打開首頁 二、打開指定頁面
- 如今要打開的是首頁,若是上一次退出的時候是首頁,則保留狀態;不然,清空原來的頁面棧,打開首頁(至關於執行 wx.reLaunch 到首頁)
- 如今要打開的是指定頁面,無論上次在什麼頁面,清空原來的頁面棧,打開指定頁面(至關於執行 wx.reLaunch 到指定頁)
小程序Page生命週期
- Page(Object) 函數用來註冊一個頁面。接受一個 Object 類型參數,其指定頁面的初始數據、生命週期回調、事件處理函數等
引用一張不錯的圖片來讓你一目瞭然,圖片來源於網絡
補充:app.onPageNotFound(Object)
基礎庫 1.9.90 開始支持,低版本需作兼容處理。
- 小程序要打開的頁面不存在時觸發,也可使用 wx.onPageNotFound 綁定監聽
- 開發者能夠在回調中進行頁面重定向,但必須在回調中同步處理,異步處理(例如 setTimeout 異步執行)無效
- 主要用於輪播、魔方等動態指定跳轉頁面的場景,避免出現頁面不存在的狀況
App({
onPageNotFound(res) {
// 能夠封裝一個小程序跳轉函數,智能解決tabbar頁面跳轉的問題
wx.redirectTo({
url: 'pages/...'
})
}
})
生命週期執行順序
生命週期執行順序
分別瞭解了App和Page的生命週期函數,那他們之間有何關聯
吐槽一句,好多文章說什麼app的生命週期函數onLauch可能會在page的onLoad以後觸發,搞得我一臉懵逼,說話這麼不嚴謹,真的好嗎???json
先看看正常的生命週期
App({
onLaunch() {
console.log('app---onLaunch');
},
onShow() {
console.log('app---onShow');
},
onHide() {
console.log('app---onHide');
}
})
Page({
onLoad(options) {
console.log('page---onLoad');
},
onReady() {
console.log('page---onReady');
},
onShow() {
console.log('page---onShow');
},
onHide() {
console.log('page---onHide');
},
onUnload() {
console.log('page---onUnload');
}
})
控制檯輸出
應該說永遠是這個順序,如今再加點代碼,就用官方的例子
App({
onLaunch() {
console.log('app---onLaunch');
// 獲取用戶信息
wx.getSetting({
success: res => {
if (res.authSetting['scope.userInfo']) {
// 已經受權,能夠直接調用 getUserInfo 獲取頭像暱稱,不會彈框
wx.getUserInfo({
success: res => {
// 能夠將 res 發送給後臺解碼出 unionId
this.globalData.userInfo = res.userInfo
console.log('app---onLaunch---success');
// 因爲 getUserInfo 是網絡請求,可能會在 Page.onLoad 以後才返回
// 因此此處加入 callback 以防止這種狀況
if (this.userInfoReadyCallback) {
this.userInfoReadyCallback(res)
}
}
})
}
}
})
}
})
Page({
onLoad(options) {
console.log('page---onLoad');
if (app.globalData.userInfo) {
this.setData({
userInfo: app.globalData.userInfo,
hasUserInfo: true
})
} else if (this.data.canIUse) {
// 因爲 getUserInfo 是網絡請求,可能會在 Page.onLoad 以後才返回
// 因此此處加入 callback 以防止這種狀況
app.userInfoReadyCallback = res => {
this.setData({
userInfo: res.userInfo,
hasUserInfo: true
})
}
} else {
// 在沒有 open-type=getUserInfo 版本的兼容處理
wx.getUserInfo({
success: res => {
app.globalData.userInfo = res.userInfo
this.setData({
userInfo: res.userInfo,
hasUserInfo: true
})
}
})
}
}
})
控制檯輸出
- 能夠看出小程序生命週期函數並無錯亂,只是加了異步操做
- 你永遠不知道異步操做會排在正常週期的哪個位置
解決方案
例子中已經給出了callback的方案,如今也可使用promise來解決
1 . callback小程序
- 解釋一波:Page頁面判斷當前app.globalData.userInfo是否有值
- 若是有,說明異步操做很順利,當作同步往下操做便可
- 若是沒有,則定義一個app方法(回調函數)app.userInfoReadyCallback = res => {...}
- 由於Page.onLoad沒有順利拿到app.globalData.userInfo,說明App頁面請求success異步操做滯後了
- 此時APP頁面不只會給globalData.userInfo賦值,還會執行Page頁面定義的回調方法,完成業務邏輯
以上例子是官方對於受權以後的操做流程示例,實際使用替換本身的初始化函數和業務邏輯api
注意事項
app.onLaunch全局只觸發一次,也就是打開的第一個頁面會有異步問題,打開第二個頁面確定是能夠拿到初始化數據的,可是小程序能夠從不一樣場景進入,可能打開的並不是是首頁,這時就須要給每個可能被第一次就打開的頁面,寫回調函數,暫時沒想到相似vue那種設置全局路由回調的方案
2 . promisepromise
- 最終效果應該和回調同樣,以前小程序不支持promise,一直沒有好好用過,等之後單獨研究~
Page實例生命週期
先來看一張很熟悉的圖
官方原話(如下內容你不須要立馬徹底弄明白,不過之後它會有幫助)
咋一看,什麼鬼,好吧~我聽你的,之後再說~
- 轉眼間,之後就到啦!好在有大佬解釋過了,借花獻佛,誰讓我是搬運工呢(最後會貼上參考文章連接)
Page實例由兩大線程組成
- 負責界面的線程(view thread)和服務線程(appservice thread),各司其職又互相配合
- 界面線程有四大狀態:
- 初始化狀態:初始化界面線程所須要的工做,包括工做機制,基本和咱們開發者沒有關係,等初始化完畢就向 「服務線程」發送初始化完畢信號,而後進入等待傳回初始化數據狀態
- 首次渲染狀態:收到「服務線程」發來的初始化數據後(就是 json和js中的data數據),就開始渲染小程序界面,渲染完畢後,發送「首次渲染完畢信號」給服務線程,並將頁面展現給用戶
- 持續渲染狀態:此時界面線程繼續一直等待「服務線程」經過this.setdata()函數發送來的界面數據,只要收到就從新局部渲染,也所以只要更新數據併發送信號,界面就自動更新
- 結束狀態:你懂得
- 初始化狀態:無需和其餘模塊交流,跟小程序開發也沒多大關聯,此階段就是啓動服務線程所需的基本功能,好比信號發送模塊。系統的初始化工做完畢,就調用自定義的onload和onshow,
而後等待界面線程的「界面線程初始化完成」信號。
onload是隻會首次渲染的時候執行一次,onshow是每次界面切換都會執行,簡單理解,這就是惟一差異微信
- 等待激活狀態:接收到「界面線程初始化完成」信號後,將初始化數據發送給「界面線程」,等待界面線程完成初次渲染
- 激活狀態:收到界面線程發送來的「首次渲染完成」信號後,就進入激活狀態既程序的正常運行狀態,並調用自定義的onReady()函數。
此狀態下就能夠經過 this.setData 函數發送界面數據給界面線程進行局部渲染,更新頁面網絡
- 後臺運行狀態:若是界面進入後臺,服務線程就進入後臺運行狀態,從目前的官方解讀來講,這個狀態挺奇怪的,和激活狀態是相同的,也能夠經過setdata函數更新界面的
總結一下
小程序的一輩子
- 打開小程序 -> app.onLaunch -> app.onShow -> Page.onLoad -> Page.onShow -> Page.onReady
- 進入下一個頁面 -> Page.onHide -> Next.onLoad -> Next.onShow -> Next.onReady
- 返回上一個頁面 -> Next.onUnload -> Page.onShow
- 離開小程序 -> app.onHide
- 再次進入 -> app未銷燬 ->app.onShow 不然從頭開始(銷燬判斷看上文運行機制)
注意:Tabbar頁面初始化以後不會被銷燬,也就是隻會執行一次 onLoad 函數
只觸發一次的,通常都是用來初始化操做
- onLaunch:初始化全局數據,注意異步問題
- onLoad:初始化頁面數據
- onReady:表明頁面已經準備穩當,界面內容的修改,最好放在這裏,如wx.setNavigationBarTitle
- onUnload:清除定時器,由於全部頁面的腳本邏輯都跑在同一個JsCore線程
觸發屢次的,通常用來改變狀態
參考文獻
http://www.wxapp-union.com/ar...
http://www.wxapp-union.com/ar...
https://developers.weixin.qq....