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