為您提供各行業(yè)互聯(lián)網(wǎng)私人定制開發(fā)解決方案
微信小程序從入門到精通(二) 小程序的能力
上一篇blog介紹了小程序的一些基礎(chǔ)概念和代碼構(gòu)成以及主要的幾個(gè)文件類型( 微信小程序從入門到精通(一) 基礎(chǔ)知識與代碼構(gòu)成),那么本篇blog就繼續(xù)圍繞官方的簡易教程以及QuickStart項(xiàng)目來繼續(xù)研究一下小程序如何工作。
上次我們具體了解小程序的代碼構(gòu)成以及四個(gè)重要的文件類型(wxml、wxss、js和app.json),那么接下來就看看小程序的整體工作運(yùn)行流程,首先從啟動(dòng)開始。
首先來看一下小程序啟動(dòng)后首先會發(fā)生什么事情,引用官方文檔的說明:
小程序啟動(dòng)之后,在 app.js 定義的 App 實(shí)例的 onLaunch 回調(diào)會被執(zhí)行。
App({ onLaunch: function () { // 小程序啟動(dòng)之后 觸發(fā) } })12345
如上所示,這里又提到了一個(gè)文件——app.js,之前我們了解了app.json(全局配置,包含頁面路徑等)和app.wxss(全局樣式),那么同理app.js也是一個(gè)用于描述公共邏輯代碼的文件,是小程序中必須的兩個(gè)主配置文件之一(app.json和app.js):
這里暫且只需要明確一個(gè)小程序的主體必定是有3個(gè)文件構(gòu)成的,分別是app.js(必須)、app.json(必須)和app.wxss(可選),明確了這一點(diǎn)之后我們繼續(xù)來看剛才官方文檔中提到的App實(shí)例,也就是在上面的代碼中看到的App{()}
,這里就牽扯到了另一個(gè)重要概念——注冊程序,屬于邏輯層的操作(App Service),官方文檔中有這樣一句話:
增加 App 和 Page 方法,進(jìn)行程序和頁面的注冊。
如上所示,Page方法我們之前已經(jīng)見過了,WXML 中的動(dòng)態(tài)數(shù)據(jù)均來自對應(yīng) Page的data,用于注冊一個(gè)頁面,關(guān)于Page我們后面再說,但我們需要知道App和Page方法是邏輯層(App Service)最核心的兩個(gè)方法,接下來我們首先具體研究一下App方法,引用官方文檔的概述:
App() 函數(shù)用來注冊一個(gè)小程序。接受一個(gè) object 參數(shù),其指定小程序的生命周期函數(shù)等。
如上所示,object參數(shù)我們需要注意,在上面提到的onLaunch 回調(diào)就是這個(gè)object參數(shù)之一,上面示例代碼的注釋中說onLaunch會在小程序啟動(dòng)后觸發(fā),那么它會被調(diào)用幾次?依舊從官方文檔中來尋找答案,我們看一下App函數(shù)的object參數(shù)的所有屬性:
屬性 | 類型 | 描述 | 觸發(fā)時(shí)機(jī) |
---|---|---|---|
onLaunch | Function | 生命周期函數(shù)–監(jiān)聽小程序初始化 | 當(dāng)小程序初始化完成時(shí),會觸發(fā) onLaunch(全局只觸發(fā)一次) |
onShow | Function | 生命周期函數(shù)–監(jiān)聽小程序顯示 | 當(dāng)小程序啟動(dòng),或從后臺進(jìn)入前臺顯示,會觸發(fā) onShow |
onHide | Function | 生命周期函數(shù)–監(jiān)聽小程序隱藏 | 當(dāng)小程序從前臺進(jìn)入后臺,會觸發(fā) onHide |
onError | Function | 錯(cuò)誤監(jiān)聽函數(shù) | 當(dāng)小程序發(fā)生腳本錯(cuò)誤,或者 api 調(diào)用失敗時(shí),會觸發(fā) onError 并帶上錯(cuò)誤信息 |
其他 | Any | 開發(fā)者可以添加任意的函數(shù)或數(shù)據(jù)到 Object 參數(shù)中,用 this 可以訪問 |
如上所示,可以看到onLaunch函數(shù)主要是做小程序的全局初始化工作,因?yàn)橹粫|發(fā)一次,類似于在android開發(fā)中的application中設(shè)置一個(gè)靜態(tài)方法,在上面的表格中注意下描述列,onLaunch是一個(gè)生命周期函數(shù),所以這里有必要再具體說明一下微信小程序中關(guān)于生命周期的一些概念,小程序的生命周期需要分為兩部分來說,分別是:
應(yīng)用的生命周期
頁面的生命周期
這就不像在android中生命周期僅針對于界面(Activity、Fragment),在小程序的官方文檔中并沒有對生命周期做單獨(dú)介紹,而是以注冊程序和注冊頁面這兩個(gè)標(biāo)題來介紹了App()函數(shù)和Page()函數(shù),然后分別在各自(App、Page)的object參數(shù)說明中以上面的表格的形式簡單標(biāo)記出了哪些是生命周期函數(shù),關(guān)于Page后面再說,先回歸正題,App()很明顯有3個(gè)生命周期函數(shù),分別是:
onLaunch
onShow
onHide
如上所示,除了onLaunch僅僅會執(zhí)行一次之外,onShow和onHide都會根據(jù)生命周期的變化被多次執(zhí)行,注意下在onShow觸發(fā)時(shí)機(jī)中提到了一個(gè)前臺和后臺的概念,其實(shí)很簡單,跟android的那幾個(gè)容易混淆的onResume、onPause、onStop等等相比容易多了,引用官方文檔的原話:
當(dāng)用戶點(diǎn)擊左上角關(guān)閉,或者按了設(shè)備 Home 鍵離開微信,小程序并沒有直接銷毀,而是進(jìn)入了后臺;當(dāng)再次進(jìn)入微信或再次打開小程序,又會從后臺進(jìn)入前臺。
就是這么簡單,那么小程序什么時(shí)候會被銷毀?官方文檔是這樣說的:
只有當(dāng)小程序進(jìn)入后臺一定時(shí)間,或者系統(tǒng)資源占用過高,才會被真正的銷毀。
先簡單了解一下,至于具體在什么場景銷毀,我們以后再做詳細(xì)說明,否則一次牽引出太多的點(diǎn)容易導(dǎo)致學(xué)習(xí)流程沒有主線,再次回到我們的主題onLaunch函數(shù),QuickStart項(xiàng)目中的onLaunch內(nèi)容略微有些復(fù)雜,我們簡單看一下:
//app.jsApp({ onLaunch: function () { // 展示本地存儲能力 var logs = wx.getStorageSync('logs') || [] logs.unshift(Date.now()) wx.setStorageSync('logs', logs) // 登錄 wx.login({ success: res => { // 發(fā)送 res.code 到后臺換取 openId, sessionKey, unionId } }) // 獲取用戶信息 wx.getSetting({ success: res => { if (res.authSetting['scope.userInfo']) { // 已經(jīng)授權(quán),可以直接調(diào)用 getUserInfo 獲取頭像昵稱,不會彈框 wx.getUserInfo({ success: res => { // 可以將 res 發(fā)送給后臺解碼出 unionId this.globalData.userInfo = res.userInfo // 由于 getUserInfo 是網(wǎng)絡(luò)請求,可能會在 Page.onLoad 之后才返回 // 所以此處加入 callback 以防止這種情況 if (this.userInfoReadyCallback) { this.userInfoReadyCallback(res) } } }) } } }) }, globalData: { userInfo: null } })123456789101112131415161718192021222324252627282930313233343536373839
根據(jù)注釋我們可以大概看出來在onLaunch函數(shù)中做了幾件事情,我們以第一個(gè)“展示本地存儲能力”為例,詳細(xì)分析一下4~7行的代碼:
// 展示本地存儲能力var logs =wx.getStorageSync('logs') || [] logs.unshift(Date.now()) wx.setStorageSync('logs', logs)1234
如上所示,調(diào)用了wx.getStorageSync和wx.setStorageSync這2個(gè)API,這就是小程序?yàn)槲覀兲峁┑?strong>本地存儲API,早期的移動(dòng)web開發(fā)中本地存儲只能用cookie的方式解決,但是cookie大小限制在4K,而且某些瀏覽器還存在cookie個(gè)數(shù)限制,而后隨著H5的發(fā)展本地存儲可以通過localStorage這個(gè)東東解決,但也僅是IE8以后才能支持,大小有5M,這就解決了很大一部分的存儲容量問題,而我們微信小程序的官方文檔中說了:
同一個(gè)微信用戶,同一個(gè)小程序 storage 上限為 10MB。
OK,本地緩存容量上又進(jìn)一步提升了,現(xiàn)在看一下wx.getStorageSync和wx.setStorageSync的說明以及用法:
wx.setStorageSync(KEY,DATA)
將 data 存儲在本地緩存中指定的 key 中,會覆蓋掉原來該 key 對應(yīng)的內(nèi)容,這是一個(gè)同步接口。
wx.getStorageSync(KEY)
從本地緩存中同步獲取指定 key 對應(yīng)的內(nèi)容。
如上所示,使用方法很簡單,同android的SharedPreferences用法類似,都是以key/value的形式去存取數(shù)據(jù),注意這兩個(gè)是同步接口,API中也有兩個(gè)異步的版本(wx.setStorage和wx.getStorage),這兩個(gè)異步接口參數(shù)相對就多一些了,以后再說,回到代碼中來,現(xiàn)在看這3行代碼就比較清晰了,首先通過獲取本地緩存中key為logs所對應(yīng)的值,如果為0或者null(首次啟動(dòng))就返回一個(gè)空數(shù)組,然后在這個(gè)空數(shù)組中追加一個(gè)元素(當(dāng)前時(shí)間),最后將當(dāng)前時(shí)間存入本地緩存,key為logs。說白了這就是記錄一個(gè)啟動(dòng)日志,首次啟動(dòng)會創(chuàng)建一個(gè)新數(shù)組,之后每次啟動(dòng)都會給這個(gè)數(shù)組追加當(dāng)前時(shí)間,所以我們在模擬器中的index頁面點(diǎn)擊頭像后跳轉(zhuǎn)到logs頁面看到的時(shí)間列表就是從緩存讀取的:
關(guān)于onLaunch函數(shù)還有一點(diǎn)需要說的就是它默認(rèn)是帶有一個(gè)參數(shù)的,包括onShow方法:
App({ onLaunch: function(options) { // Do something initial when launch. }, onShow: function(options) { // Do something when show. }, onHide: function() { // Do something when hide. }, onError: function(msg) { console.log(msg) }, globalData: 'I am global data'})123456789101112131415
如上所示,那么這個(gè)options參數(shù)都包含哪些屬性呢?官方文檔列出了7個(gè)屬性(path、query、scene、shareTicket、referrerInfo、referrerInfo.appId、referrerInfo.extraData),下面只列舉2個(gè)先簡單了解下,我們在onLaunch函數(shù)中打印一下options的path和sence屬性:
path是打開小程序的路徑,scene是打開小程序的場景值,關(guān)于場景值的概念以后再說,此處簡單了解一下即可,接下來要說一個(gè)比較重要的函數(shù)getApp()
,它可以用來獲取到小程序?qū)嵗?,例如獲取全局變量globalData:
// other.jsvar appInstance = getApp() console.log(appInstance.globalData) // I am global data123
如上所示,關(guān)于小程序啟動(dòng)相關(guān)的內(nèi)容先說這么多,接下來看一下頁面相關(guān)的內(nèi)容。
接下來要說的就是本篇blog的重點(diǎn)內(nèi)容了,注冊頁面,也就是關(guān)于Page函數(shù)。上一篇blog我們簡單介紹了一部分關(guān)于Page的內(nèi)容,下面看一下官方文檔簡易教程中對Page的概述和一段示例代碼:
Page 是一個(gè)頁面構(gòu)造器,這個(gè)構(gòu)造器就生成了一個(gè)頁面。在生成頁面的時(shí)候,小程序框架會把 data 數(shù)據(jù)和 index.wxml 一起渲染出最終的結(jié)構(gòu),于是就得到了你看到的小程序的樣子。在渲染完界面之后,頁面實(shí)例就會收到一個(gè) onLoad 的回調(diào),你可以在這個(gè)回調(diào)處理你的邏輯。
Page({ data: { // 參與頁面渲染的數(shù)據(jù) logs: [] }, onLoad: function () { // 頁面渲染后 執(zhí)行 } })12345678
如上所示,之所以說Page重要,是因?yàn)樗淖饔玫?strong>構(gòu)造頁面,而我們的小程序就是通過一個(gè)個(gè)頁面組成的,Page函數(shù)的2個(gè)核心點(diǎn)分別是data和onLoad回調(diào),data負(fù)責(zé)提供數(shù)據(jù),onLoad則是處理頁面渲染后的邏輯。接下來我們就具體看一下Page函數(shù),我們在IDE中新建一個(gè)js文件后寫一個(gè)字母P首先看到的就是Page函數(shù)的自動(dòng)補(bǔ)全提示:
如上圖,緊接著會自動(dòng)生成一個(gè)完整的Page函數(shù)模板,代碼如下:
Page({ /** * 頁面的初始數(shù)據(jù) */ data: { }, /** * 生命周期函數(shù)--監(jiān)聽頁面加載 */ onLoad: function (options) { }, /** * 生命周期函數(shù)--監(jiān)聽頁面初次渲染完成 */ onReady: function () { }, /** * 生命周期函數(shù)--監(jiān)聽頁面顯示 */ onShow: function () { }, /** * 生命周期函數(shù)--監(jiān)聽頁面隱藏 */ onHide: function () { }, /** * 生命周期函數(shù)--監(jiān)聽頁面卸載 */ onUnload: function () { }, /** * 頁面相關(guān)事件處理函數(shù)--監(jiān)聽用戶下拉動(dòng)作 */ onPullDownRefresh: function () { }, /** * 頁面上拉觸底事件的處理函數(shù) */ onReachBottom: function () { }, /** * 用戶點(diǎn)擊右上角分享 */ onShareAppMessage: function () { } })1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465
注釋也都比較清晰,可以看到Page函數(shù)有5個(gè)生命周期函數(shù)(onLoad、onReady、onShow、onHide、onUnload),相比較App函數(shù)(onLaunch、onShow、onHide)多出了2個(gè),同樣的Page函數(shù)也支持我們自定義函數(shù)或者數(shù)據(jù),就如同上一篇blog中寫的那個(gè)clickMe函數(shù),下面我們按順序依次了解一下,首先是data,data在官方文檔中的定義是初始化數(shù)據(jù),文檔中是這樣描述的:
初始化數(shù)據(jù)將作為頁面的第一次渲染。data 將會以 JSON 的形式由邏輯層傳至渲染層,所以其數(shù)據(jù)必須是可以轉(zhuǎn)成 JSON 的格式:字符串,數(shù)字,布爾值,對象,數(shù)組。
如上所示,這里我們注意兩點(diǎn),分別是第一次渲染和JSON格式。第一次渲染的意思就是在onLoad函數(shù)被調(diào)用之前的頁面渲染,而JSON格式則是定死了data中的數(shù)據(jù)類型只能是以下幾種(String,Number,Boolean,Object,Array),下面看一下QuickStart項(xiàng)目中Page函數(shù)的data部分:
Page({ data: { motto: 'Hello World', userInfo: {}, hasUserInfo: false, canIUse: wx.canIUse('button.open-type.getUserInfo') } })12345678
如上所示,這里定義的初始化數(shù)據(jù)比較簡單,字符串motto直接通過wxml中的{{motto}}渲染在頁面并顯示,Object類型的用戶信息(userInfo),具體講是一個(gè)JSON對象,這里初始化為空,因?yàn)闀趏nLoad函數(shù)中賦值,Boolean類型的是否存在用戶信息標(biāo)志(hasUserInfo)以及Boolean類型的“判斷小程序的API,回調(diào),參數(shù),組件等是否在當(dāng)前版本可用”的標(biāo)志。看完了data接下來看一下更重要的生命周期函數(shù),首先是最重要的onLoad函數(shù),它與App()中的onLaunch函數(shù)相對應(yīng),后者是小程序啟動(dòng)后全局觸發(fā)一次,前者則是一個(gè)頁面只會調(diào)用一次,所以這個(gè)onLoad也就相當(dāng)于android中Activity的onCreate,依舊看一下QuickStart項(xiàng)目中的onLoad函數(shù),由于index.js中的onLoad略微復(fù)雜,我們先以log.js中的onLoad函數(shù)為例:
Page({ data: { logs: [] }, onLoad: function () { this.setData({ logs: (wx.getStorageSync('logs') || []).map(log => { return util.formatTime(new Date(log)) }) }) } })123456789101112
這個(gè)例子就很好,用到了我們上面提到過的本地緩存(wx.getStorageSync)和data,可以看到data中定義了一個(gè)空數(shù)組,然后在onLoad函數(shù)中做了賦值操作(setData),所以這里我們可以再仔細(xì)體會一遍data和onLoad各自的作用和關(guān)系。說到了setData,下面就詳細(xì)說明一下這個(gè)函數(shù)Page.prototype.setData()
,首先看一下官方文檔中的概述:
setData 函數(shù)用于將數(shù)據(jù)從邏輯層發(fā)送到視圖層(異步),同時(shí)改變對應(yīng)的 this.data 的值(同步)。
如上所示,可以看到setData函數(shù)做了兩件事情,首先是把數(shù)據(jù)以異步形式發(fā)送到視圖層,也就是說可以理解為對頁面進(jìn)行的第二次渲染,然后以同步形式改變了this.data的值,也就是在這里同步修改了初始化數(shù)據(jù),這里再強(qiáng)調(diào)一點(diǎn),同App()函數(shù)一樣,Page()函數(shù)的參數(shù)也支持任意類型的自定義函數(shù)或數(shù)據(jù),同樣是通過this調(diào)用,下面看一下官方文檔給出的幾個(gè)小例子:
//index.jsPage({ data: { text: 'init data', num: 0, array: [{text: 'init data'}], object: { text: 'init data' } }, changeText: function() { // this.data.text = 'changed data' // bad, it can not work this.setData({ text: 'changed data' }) }, changeNum: function() { this.data.num = 1 this.setData({ num: this.data.num }) }, changeItemInArray: function() { // you can use this way to modify a danamic data path this.setData({ 'array[0].text':'changed data' }) }, changeItemInObject: function(){ this.setData({ 'object.text': 'changed data' }); }, addNewField: function() { this.setData({ 'newField.text': 'new data' }) } })123456789101112131415161718192021222324252627282930313233343536373839
注意下第12行的注釋代碼,不能直接通過this.data.key = “xxx”去改變data中的數(shù)據(jù),必須要通過setData方法才能改變。在上面的例子中我們依次列舉了修改以下幾種類型的數(shù)據(jù):
字符串 line11~16
數(shù)字 line17~22
數(shù)組 line23~28
對象 line29~38
如上所示,需要注意的是再修改data為Object類型的數(shù)據(jù)時(shí),setData中參數(shù)對象的key必須是字符串形式,例如'object.text': 'changed data'
,說了這么多關(guān)于Page的生命周期函數(shù)也只講了1個(gè)——onLoad,關(guān)于剩下的幾個(gè)非重點(diǎn)函數(shù)(onShow、onReady、onHide和onUnload)以后再說,否則會導(dǎo)致篇幅過長。跳過這幾個(gè)生命周期函數(shù),那我們的Page函數(shù)還剩下幾個(gè)事件處理函數(shù),我們以下拉刷新(onPullDownRefresh)和上拉觸底(onReachBottom)為例詳細(xì)說明一下。依舊先看一下官方文檔對這兩個(gè)函數(shù)的詳細(xì)說明:
如上所示,首先是下拉刷新,很簡單,監(jiān)聽用戶下拉刷新動(dòng)作,上一篇blog也提到了需要在app.json中配置開啟下拉刷新后才行,看以一個(gè)簡單的例子:
Page({ onPullDownRefresh:function(){ wx.showToast({ title: '執(zhí)行了下拉刷新動(dòng)作~', icon: 'none', duration: 2000 }) } })123456789
這里又用到了一個(gè)簡單的交互反饋API——顯示消息提示框,跟android中的吐司基本一樣,用法也很簡單,title是提示內(nèi)容,duration是延遲時(shí)間(單位毫秒),icon是圖標(biāo),可以設(shè)置為“success”、“l(fā)oading”和“none”,下面看一下下拉的效果圖:
如上所示,而上拉觸底則需要在scroll-view組件中去滑動(dòng)才能觸發(fā)諸如滾動(dòng)條滾動(dòng)、觸底、觸頂等事件,牽扯到組件的問題我們后面再說。
本篇blog重點(diǎn)介紹了注冊程序(App函數(shù))和注冊頁面(Page函數(shù))的相關(guān)用法以及setData函數(shù)的用法,主要還停留在基礎(chǔ)理性的部分,等后面學(xué)習(xí)了組件之后就可以嘗試做一些東西了,由于以前js基礎(chǔ)不是特別好所以在學(xué)習(xí)過程中會遇到各種各樣的js語法糖,那就開開心心的當(dāng)補(bǔ)習(xí)js基礎(chǔ)知識好了!先到這里吧,The End。