準(zhǔn)備寫(xiě)一個(gè)小系列,幫大家梳理一下微信小程序整體的技術(shù)架構(gòu)。 盡量做到用最短的篇幅說(shuō)明問(wèn)題,幫助大家快速了解這個(gè)平臺(tái)。 這次就先說(shuō)說(shuō)小程序的生命周期。
關(guān)于生命周期,這在很多開(kāi)發(fā)生態(tài)周都會(huì)有,比如原生的 iOS 和 Android 開(kāi)發(fā)中都有類(lèi)似的概念。
這次咱們就詳細(xì)了解一下小程序的生命周期。以及兩個(gè)核心概念 —— App 和 Page。
App 函數(shù)是整個(gè)小程序的入口, 這在我們上篇文章中已經(jīng)介紹了, 咱們?cè)侔汛a貼出來(lái)回顧一遍:
App({
onLaunch: function () {
//調(diào)用API從本地緩存中獲取數(shù)據(jù)
var logs = wx.getStorageSync('logs') || []
logs.unshift(Date.now())
wx.setStorageSync('logs', logs)
},
getUserInfo:function(cb){
var that = this
if(this.globalData.userInfo){
typeof cb == "function" && cb(this.globalData.userInfo)
}else{
//調(diào)用登錄接口
wx.login({
success: function () {
wx.getUserInfo({
success: function (res) {
that.globalData.userInfo = res.userInfo
typeof cb == "function" && cb(that.globalData.userInfo)
}
})
}
})
}
},
globalData:{
userInfo:null
}
})
大體上就是這樣了, 微信開(kāi)發(fā)工具的默認(rèn)項(xiàng)目中定義了 onLaunch 方法, 它就是一個(gè)生命周期方法。 除了這個(gè)方法之外, 還有另外兩個(gè)生命周期方法, 這里給大家列出:
onLaunch: 小程序初始化完成。
onShow: 小程序顯示,初始化完成后會(huì)調(diào)用, 從后臺(tái)切換進(jìn)來(lái)也會(huì)調(diào)用。
onHide: 小程序隱藏,用戶(hù)在微信中點(diǎn)后退按鈕算隱藏, 用戶(hù)按 Home 鍵切回桌面也算隱藏, 這時(shí) onHide 方法都會(huì)被調(diào)用。
這就是 App 目前開(kāi)放的所有的生命周期方法了。根據(jù)你的小程序業(yè)務(wù)邏輯,使用這些生命周期方法即可。
微信還給我們提供了 getApp 函數(shù), 這是一個(gè)全局函數(shù), 在任何地方調(diào)用這個(gè)函數(shù)都可以得到 App 的實(shí)例,供我們使用。 這樣我們?cè)谛枰褂?App 對(duì)象相關(guān)屬性的時(shí)候,就可以引用到它了。
Page 是小程序中另外一個(gè)重要的對(duì)象, 它表示小程序的每一個(gè)單獨(dú)的頁(yè)面。 它也包含生命周期。 它的方法如下:
data: 表示頁(yè)面的數(shù)據(jù), 用于前端 UI 顯示數(shù)據(jù)。
onLoad: 頁(yè)面加載完成。
onReady: 頁(yè)面渲染完成。
onShow: 頁(yè)面顯示。
onHide: 頁(yè)面隱藏。
onUnload: 頁(yè)面卸載。
Page 的基本結(jié)構(gòu)如下:
Page({
data: {
message: "Hello world."
},
onLoad: function(options) {
// Do some initialize when page load.
},
onReady: function() {
// Do something when page ready.
},
onShow: function() {
// Do something when page show.
},
onHide: function() {
// Do something when page hide.
},
onUnload: function() {
// Do something when page close.
}
})
當(dāng)前端頁(yè)面需要顯示數(shù)據(jù)的時(shí)候, 可以這樣:
<!--
<view>{{message}}</view>
-->
這里面的 message 就是引用的 Page 對(duì)象中,我們定義在 data 屬性中的 key 所對(duì)應(yīng)的內(nèi)容了。 這樣 Page 以及它的數(shù)據(jù)如何與前端顯示綁定起來(lái)的流程咱們就明白了。
接下來(lái), 我們?cè)趺刺幚砬岸?UI 上面的用戶(hù)操作事件呢? 比如用戶(hù)點(diǎn)擊, 我們可以使用 bindtap 屬性:
<!--
<view bindtap="viewTapped" >{{message}}</view>
-->
bindtap 屬性指定了一個(gè)方法名,我們只需要在 Page 對(duì)象里面實(shí)現(xiàn)這個(gè)方法就可以處理用戶(hù)的點(diǎn)擊操作了:
Page({
viewTapped: function() {
console.log('hello world.')
}
})
當(dāng)然, Page 還提供了修改它內(nèi)部數(shù)據(jù)的方法 —— setData, 我們可以在剛才這個(gè)點(diǎn)擊事件中使用 setData 修改顯示在前端中的文字:
Page({
viewTapped: function() {
this.setData({
message: '你好, 世界'
});
}
})
微信小程序的 UI 層如何與控制層交互, 以及怎么樣響應(yīng)用戶(hù)操作事件, 這次咱們來(lái)聊聊。
微信小程序使用 WXML 文件作為 UI 視圖。 WXML 其實(shí)就是 WeiXin Markup Language 的縮寫(xiě)。 是一種類(lèi)似 XML 語(yǔ)法結(jié)構(gòu)的 UI 定義方式。
它可以進(jìn)行數(shù)據(jù)綁定, 顯示 Page 中相應(yīng)的屬性:
<!--
<view> {{message}} </view>
-->
兩對(duì)括號(hào)中的 message 對(duì)應(yīng) Page 中 data 屬性中的 message:
Page({
data: {
message: 'Hello World'
}
})
當(dāng)然, 我們還可以進(jìn)行循環(huán)顯示, 這個(gè)語(yǔ)法可能會(huì)和大家平時(shí)使用過(guò)的模板語(yǔ)法不太相同:
<!--
<view wx:for="{{array}}"> {{item}} </view>
-->
wx:for 是微信定義的一個(gè)特殊屬性, 其實(shí)相當(dāng)于一個(gè) for 循環(huán), 重復(fù)顯示 View 空間, 然后將數(shù)組中的每一個(gè)元素都遍歷一遍, 下面是 array 在 Page 中的定義:
Page({
data: {
array: ["Swift", "Objctive-C", "Javascript"]
}
})
上面 WXML 例子中的 代表我們 data 中的 array 數(shù)組, 而 表示的就是數(shù)組遍歷中的每一個(gè)元素, 編譯運(yùn)行后的界面顯示如下:
除了 wx:for 這種循環(huán)遍歷語(yǔ)法之外, 微信還提供了另外一種, wx:if, 大家從名稱(chēng)中應(yīng)該就可以想到, 它是一個(gè)條件判斷, 只有當(dāng)條件判斷成立, 才會(huì)顯示這個(gè) View。 除了 wx:if 之外, 還提供了了 wx:elif, wx:else
<!--
<view wx:if="{{false}}" >Hello Title</view>
<view wx:elif="{{false}}" >Hello World</view>
<view wx:else >Hello Wechat</view>
-->
注意這三個(gè)屬性的用法, wx:if 和 wx:elif 這兩個(gè)屬性后面需要跟隨一個(gè)布爾表達(dá)式用于判斷, 而 wx:else 后面不需要跟隨任何表達(dá)式。 這三個(gè)屬性的邏輯和我們平時(shí)開(kāi)發(fā)中的 if else 判斷邏輯完全一樣,也比較好理解。
關(guān)于 wx:else 微信官方的示例中是給他指定了表達(dá)式屬性的。 但我在真實(shí)環(huán)境上測(cè)試了一下, wx:else 無(wú)論是否指定后面的屬性值,最終的輸出結(jié)果都是一樣的, 所以這可能是官方示例的一個(gè)筆誤。 按照 if 表達(dá)式正常的邏輯來(lái)看,應(yīng)該不需要給它再設(shè)屬性了。 大家可以留意一下。
說(shuō)完了循環(huán)和條件判斷語(yǔ)法, WXML 還支持模板定義, 比如這樣:
<!--
<template name="hello" >
<view>
Hello, {{name}}
</view>
</template>
<template is="hello" data="{{...swift}}" ></template>
<template is="hello" data="{{...objc}}" ></template>
-->
簡(jiǎn)單解釋一下, 第一個(gè) template 標(biāo)簽是模板定義, name 屬性定義了它的名字,在兩個(gè)標(biāo)簽之間是它的內(nèi)部結(jié)構(gòu)。 然后緊接著的后兩個(gè) template 標(biāo)簽是模板的引用, is 屬性代表要引用哪個(gè)模板。 我們這里填入的都是 hello, 也就是我們最開(kāi)始定義的這個(gè)模板結(jié)構(gòu)。 然后就是 data 屬性,通過(guò)它傳入模板需要的相關(guān)數(shù)據(jù)。 先來(lái)開(kāi)一下 swift 和 objc 這兩個(gè)數(shù)據(jù)的定義:
Page({
data: {
swift: { name: "Swift"},
objc: { name: "Objective-C"}
}
})
swift 和 objc 都是兩個(gè) JSON 對(duì)象, 都包含一個(gè) name 屬性。 大家注意看我們前面模板引用 data 屬性的方式:
<!-- <template is="hello" data="{{...swift}}" ></template> -->
這里我們?cè)谝粚?duì)大括號(hào)中的變量名前面還寫(xiě)了三個(gè)點(diǎn) —— …
… 其實(shí)是一個(gè)操作符, 用于將 swift 變量?jī)?nèi)部的值 “展開(kāi)”, 這么說(shuō)可能不太好理解, 咱們還回到模板的定義中:
<!--
<template name="hello" >
<view>
Hello, {{name}}
</view>
</template>
-->
我們看到, template 內(nèi)部使用 來(lái)引用我們傳入對(duì)象的屬性。 這就需要我們 “展開(kāi)” 傳入的對(duì)象, 才能讓模板找到對(duì)應(yīng) key 的值。 簡(jiǎn)單來(lái)說(shuō), 如果我們不適用 … 這個(gè)操作符,而是直接像這樣傳入對(duì)象:
<!--
<template is="hello" data="{{swift}}" ></template>
-->
如果這樣運(yùn)行程序的話, 模板是不能正確讀取到 name 屬性的。
當(dāng)然, 我們可以不用把模板的定義和引用它的代碼寫(xiě)到一起, 我們可以把模板的定義單獨(dú)寫(xiě)在一個(gè) WXML 文件中, 然后在另外一個(gè)文件中使用 import 來(lái)引入它。 這也是一個(gè)結(jié)構(gòu)良好的項(xiàng)目的通常做法。 比如我們剛才的模板存放到 hello.wxml 中, 我們就可以這樣引用它:
<!--
<import src="hello.wxml" />
<template is="hello" data="{{...swift}}" ></template>
<template is="hello" data="{{...objc}}" ></template>
-->
這樣我們可以比較好的進(jìn)行模塊劃分。 當(dāng)然, 除了 import 之外, 微信還給我們提供了另外一種引用方式, include。 簡(jiǎn)而言之, 他們之間的區(qū)別是這樣, import 只能引入目標(biāo)文件中的 template 定義, 不會(huì)引入具體的 UI 內(nèi)容。 而 include 正好相反, 它只會(huì)引入目標(biāo)文件中的內(nèi)容,不會(huì)引入 template 定義, 比如我們有一個(gè)文件,叫做 header.wxml:
<!--
<view>Header Title</view>
-->
然后我們?cè)?index.wxml 中這樣引用:
<!--
<include src="header.wxml" />
<view>Hello world!</view>
-->
這樣就會(huì)引入 header.wxml 中定義的內(nèi)容了, 最后的輸出結(jié)果就是他們兩個(gè)文件中所有 UI 組件的整合了。 這就是 import 和 include 的區(qū)別了。