[TOC]
## 2.2 程序與頁面
從邏輯組成來說,一個小程序是由多個`頁面`組成的`程序`。
而通常`程序`在啟動或者退出的時候存儲數據或者在“頁面”顯示或者隱藏的時候做一些邏輯處理,
### 2.2.1 程序
宿主環境(客戶端)提供了`App()` 構造器用來注冊一個程序App
>[warning] `App()` 構造器必須寫在項目根目錄的app.js文件里
App實例是單例對象,在其他JS腳本中可以使用宿主環境提供的 `getApp()` 來獲取程序實例。
代碼清單3-3 getApp() 獲取App實例
~~~
// other.js
var appInstance = getApp()
~~~
#### 2.2.1.1 程序構造器--`App()`
App() 的調用方式如代碼清單3-4所示,App構造器接受一個Object參數,參數說明如表3-1所示,其中onLaunch / onShow / onHide 三個回調是App實例的生命周期函數,我們會在后文展開;onError我們暫時不在本章展開,我們會在第8章里詳細討論;App的其他參數我們也放在后文進行展開。
代碼清單3-4 App構造器
~~~
App({
onLaunch: function(options) {},
onShow: function(options) {},
onHide: function() {},
onError: function(msg) {},
globalData: 'I am global data'
})
~~~
:-: 表3-1 App構造器的參數
| 屬性 | 類型 | 描述 | 觸發時機 |
| --- | --- | --- | --- |
| onLaunch | Function | 生命周期函數--監聽小程序初始化 | 當小程序初始化完成時,會觸發 onLaunch(全局只觸發一次) |
| onShow | Function | 生命周期函數--監聽小程序顯示 | 當小程序啟動,或從后臺進入前臺顯示,會觸發 onShow|
| onHide | Function | 生命周期函數--監聽小程序隱藏 | 當小程序從前臺進入后臺,會觸發 onHide |
| onError | Function | 錯誤監聽函數 | 當小程序發生腳本錯誤,或者 api 調用失敗時,會觸發 onError 并帶上錯誤信息 |
| onPageNotFound | Function | 頁面不存在監聽函數 | 當小程序出現要打開的頁面不存在的情況,會帶上頁面信息回調該函數|
| 其他 | Any | | 開發者可以添加任意的函數或數據到 Object 參數中,用 this 可以訪問|
`getApp()`,全局的 getApp() 函數可以用來獲取到小程序實例。
>[warning] 1. App() 必須在 app.js 中注冊,且不能注冊多個。
> 2. 不要在定義于 App() 內的函數中調用 getApp() ,使用 this 就可以拿到 app 實例。
> 3. 不要在 onLaunch 的時候調用 getCurrentPages(),此時 page 還沒有生成。
> 4. 通過 getApp() 獲取實例之后,不要私自調用生命周期函數。
#### 2.2.1.2 程序的生命周期和打開場景
* **前臺、后臺定義**: 當用戶點擊左上角關閉,或者按了設備 Home 鍵離開微信,小程序并沒有直接銷毀,而是進入了后臺;當再次進入微信或再次打開小程序,又會從后臺進入前臺。需要注意的是:只有當小程序進入后臺一定時間,或者系統資源占用過高,才會被真正的銷毀。
初次進入小程序的時候,微信客戶端初始化好宿主環境,同時從網絡下載或者從本地緩存中拿到小程序的代碼包,把它注入到宿主環境,初始化完畢后,微信客戶端就會給App實例派發onLaunch事件,App構造器參數所定義的`onLaunch`方法會被調用。
進入小程序之后,用戶可以點擊左上角的關閉,或者按手機設備的Home鍵離開小程序,此時小程序并沒有被直接銷毀,我們把這種情況稱為“小程序進入后臺狀態”,App構造器參數所定義的`onHide`方法會被調用。
當再次回到微信或者再次打開小程序時,微信客戶端會把“后臺”的小程序喚醒,我們把這種情況稱為“小程序進入前臺狀態”,App構造器參數所定義的`onShow`方法會被調用。
>[warning] App的生命周期是由微信客戶端根據用戶操作主動觸發的。為了避免程序上的混亂,我們不應該從其他代碼里主動調用App實例的生命周期函數。
在微信客戶端中打開小程序有很多途徑:
* 從群聊會話里打開
* 從小程序列表中打開
* 通過微信掃一掃二維碼打開
* 從另外一個小程序打開當前小程序等
針對不同途徑的打開方式,小程序有時需要做不同的業務處理,所以微信客戶端會把打開方式帶給onLaunch和onShow的調用參數`options`,示例代碼以及詳細參數如代碼清單3-5和表3-2所示。需要留意小程序的宿主環境在迭代更新過程會增加不少打開場景,因此要獲取最新的場景值說明請查看[【官方文檔】](https://mp.weixin.qq.com/debug/wxadoc/dev/framework/app-service/app.html)。
代碼清單3-5 onLaunch和onShow帶參數
~~~
App({
onLaunch: function(options) { console.log(options) },
onShow: function(options) { console.log(options) }
})
~~~
:-: 表3-2 `onLaunch`,`onShow`參數
| 字段 | 類型 | 描述 |
| --- |--- | ---|
| path | String | 打開小程序的頁面路徑 |
| query | Object | 打開小程序的頁面參數query |
| scene | Number | 打開小程序的場景值,詳細場景值請參考小程序官方文檔 |
| shareTicket | String | shareTicket,詳見小程序官方文檔 |
| referrerInfo | Object | 當場景為由從另一個小程序或公眾號或App打開時,返回此字段 |
| referrerInfo.appId | String | 來源小程序或公眾號或App的 appId,詳見下方說明 |
| referrerInfo.extraData | Object | 來源小程序傳過來的數據,scene=1037或1038時支持 |
:-: 表3-3 以下場景支持返回 referrerInfo.appId
| 場景值 | 場景 | appId信息含義 |
| --- |--- | ---|
| 1020 | 公眾號 profile | 頁相關小程序列表 返回來源公眾號 appId
| 1035 | 公眾號自定義菜單 | 返回來源公眾號 appId
| 1036 | App 分享消息卡片 | 返回來源應用 appId
| 1037 | 小程序打開小程序 | 返回來源小程序 appId
| 1038 | 從另一個小程序返回 | 返回來源小程序 appId
| 1043 | 公眾號模板消息 | 返回來源公眾號 appId
`onPageNotFound`
> 基礎庫 1.9.90 開始支持,低版本需做兼容處理
當要打開的頁面并不存在時,會回調這個監聽器,并帶上以下信息:
| 字段 | 類型 | 說明 |
| --- |--- | ---|
| path | String | 不存在頁面的路徑 |
| query | Object | 打開不存在頁面的 query |
| isEntryPage | Boolean | 是否本次啟動的首個頁面 (例如從分享等入口進來,首個頁面是開發者配置的分享頁面)|
開發者可以在 `onPageNotFound` 回調中進行重定向處理,但必須在回調中同步處理,異步處理(例如 setTimeout 異步執行)無效。
示例代碼:
~~~
App({
onPageNotFound(res) {
wx.redirectTo({
url: 'pages/...'
}) // 如果是 tabbar 頁面,請使用 wx.switchTab
}
})
~~~
>[warning] 1. 如果開發者沒有添加 onPageNotFound 監聽,當跳轉頁面不存在時,將推入微信客戶端原生的頁面不存在提示頁面
> 2. 如果 onPageNotFound 回調中又重定向到另一個不存在的頁面,將推入微信客戶端原生的頁面不存在提示頁面,并且不再回調 onPageNotFound
#### 2.2.1.3. 小程序全局數據
小程序的JS腳本是運行在JsCore的線程里,小程序的每個頁面各自有一個WebView線程進行渲染,所以小程序切換頁面時,小程序邏輯層的JS腳本運行上下文依舊在同一個JsCore線程中。
小程序的App實例是單例的,因此不同頁面直接可以通過App實例下的屬性來共享數據。
>[info] App構造器可以傳遞其他參數作為全局屬性以達到全局共享數據的目的。
代碼清單3-6 小程序全局共享數據
~~~
// app.js
App({
globalData: 'I am global data' // 全局共享數據
})
// 其他頁面腳本other.js
var appInstance = getApp()
console.log(appInstance.globalData) // 輸出: I am global data
~~~
>[danger] 由于所有頁面的腳本邏輯都跑在同一個JsCore線程,頁面使用setTimeout或者setInterval的定時器,然后跳轉到其他頁面時,這些定時器并沒有被清除,需要開發者自己在頁面離開的時候進行清理。
### 2.2.2 頁面
一個小程序可以有很多頁面,每個頁面承載不同的功能,頁面之間可以互相跳轉。
#### 2.2.2.1 頁面的文件構成和路徑
一個頁面是分三部分組成:
* 界面:`WXML`文件和`WXSS`文件描述
* 配置:由`JSON`文件進行描述
* 邏輯:`JS`腳本文件負責
>[danger] 一個頁面的文件需要放置在同一個目錄下,其中WXML文件和JS文件是必須存在的,JSON和WXSS文件是可選的。
>[danger] 頁面路徑需要在小程序代碼根目錄app.json中的pages字段聲明,否則這個頁面不會被注冊到宿主環境中。
1. **全局配置**
`app.json`文件用來對微信小程序進行全局配置,決定小程序各個頁面文件的路徑、窗口表現、設置網絡超時時間、設置多 tab 等。[【官方文檔詳細說明】](https://developers.weixin.qq.com/miniprogram/dev/framework/config.html)
:-: app.json 配置項列表
| 屬性 | 類型 | 必填 | 描述 |
|--- | --- |--- | --- |
| pages | String Array | 是 | 設置頁面路徑 |
| window | Object | 否 | 設置默認頁面的窗口表現 |
| tabBar | Object | 否 | 設置底部 tab 的表現 |
| networkTimeout | Object | 否 | 設置網絡超時時間 |
| debug | Boolean | 否 | 設置是否開啟 debug 模式 |
代碼清單3-7 app.json包含了所有配置選項。默認pages字段的第一個頁面路徑為小程序的首頁。
~~~
{
"pages": [
"pages/index/index", // 第一項默認為首頁
"pages/logs/index"
],
"window": {
"navigationBarTitleText": "Demo"
},
"tabBar": {
"list": [{
"pagePath": "pages/index/index",
"text": "首頁"
}, {
"pagePath": "pages/logs/logs",
"text": "日志"
}]
},
"networkTimeout": {
"request": 10000,
"downloadFile":10000
},
"debug": true
}
~~~
2. **頁面配置**
每一個小程序頁面使用`.json`文件來對本頁面的窗口表現進行配置。 頁面的配置比app.json全局配置簡單得多,只是設置 app.json 中的 window 配置項的內容,頁面中配置項會覆蓋 app.json 的 window 中相同的配置項。
頁面的.json只能設置 window 相關的配置項,以決定本頁面的窗口表現,所以無需寫 window 這個鍵,如:
| 屬性 | 類型 | 默認值 | 描述 |
| --- | --- | --- | --- |
| navigationBarBackgroundColor | HexColor | #000000 | 導航欄背景顏色,如"#000000" |
| navigationBarTextStyle | String | white | 導航欄標題顏色,僅支持 black/white |
| navigationBarTitleText | String | 導航欄標題文字內容 |
| backgroundColor | HexColor | #ffffff | 窗口的背景色 |
| backgroundTextStyle | String| dark | 下拉 loading 的樣式,僅支持 dark/light |
| enablePullDownRefresh | Boolean | false | 是否開啟下拉刷新,詳見頁面相關事件處理函數。 |
| disableScroll | Boolean | false | 設置為 true 則頁面整體不能上下滾動;只在 page.json 中有效,無法在 app.json 中設置該項|
| onReachBottomDistance | Number | 50 | 頁面上拉觸底事件觸發時距頁面底部距離,單位為px |
例如:
~~~
{
"navigationBarBackgroundColor": "#ffffff",
"navigationBarTextStyle": "black",
"navigationBarTitleText": "微信接口功能演示",
"backgroundColor": "#eeeeee",
"backgroundTextStyle": "light"
}
~~~
#### 2.2.2.2 頁面構造器--`Page()`
宿主環境提供了` Page()` 構造器用來注冊一個小程序頁面,Page()在頁面腳本`*.js`中調用,Page() 的調用方式如代碼清單3-8所示。
Page構造器(`Page()`)接受一個Object參數,指定頁面的初始數據、生命周期函數、事件處理函數等。參數說明如表3-4所示。
>[warning] Object 內容在頁面加載時會進行一次深拷貝,需考慮數據大小對頁面加載的開銷
代碼清單3-8 Page構造器
~~~
Page({
data: {
text: "This is page data."
},
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.
},
onPullDownRefresh: function() {
// Do something when pull down.
},
onReachBottom: function() {
// Do something when page reach bottom.
},
onShareAppMessage: function () {
// return custom share data when user share.
},
onPageScroll: function() {
// Do something when page scroll
},
onTabItemTap(item) {
console.log(item.index)
console.log(item.pagePath)
console.log(item.text)
},
// Event handler.
viewTap: function() {
this.setData({
text: 'Set some data for updating view.'
}, function() {
// this is setData callback
})
},
customData: {
hi: 'MINA'
}
})
~~~
:-: 表3-4 Page構造器的參數
| 參數屬性 | 類型 | 描述 |
| --- |--- | --- |
| data | Object | 頁面的初始數據 |
| onLoad | Function | 生命周期函數--監聽頁面加載,觸發時機早于onShow和onReady |
| onReady | Function | 生命周期函數--監聽頁面初次渲染完成 |
| onShow | Function | 生命周期函數--監聽頁面顯示,觸發事件早于onReady |
| onHide | Function | 生命周期函數--監聽頁面隱藏 |
| onUnload | Function | 生命周期函數--監聽頁面卸載 |
| onPullDownRefresh | Function | 頁面相關事件處理函數--監聽用戶下拉動作 |
| onReachBottom | Function | 頁面上拉觸底事件的處理函數 |
| onShareAppMessage | Function | 用戶點擊右上角轉發 |
| onPageScroll | Function | 頁面滾動觸發事件的處理函數 |
| onTabItemTap | Function | 當前是 tab 頁時,點擊 tab 時觸發 |
| 其他 | Any | 可以添加任意的函數或數據,在Page實例的其他函數中用 this 可以訪問 |
上表中的Page構造器`page()`的參數可分為4部分
* ***初始化數據***
`data`屬性是當前頁面WXML模板中可以用來做數據綁定的初始數據;初始數據將作為頁面的第一次渲染使用。
`data`將會以 JSON 的形式由邏輯層傳至渲染層,所以其數據必須是可以轉成 JSON 的格式:字符串,數字,布爾值,對象,數組。
渲染層可以通過 WXML 對數據進行綁定。
示例代碼:
~~~HTML
<!-- *.wxml -->
<view>{{text}}</view>
<view>{{array[0].msg}}</view>
~~~
~~~JavaScript
//*.js
Page({
data: {
text: 'init data',
array: [{msg: '1'}, {msg: '2'}]
}
})
~~~
* ***生命周期函數***
`onLoad / onReady / onShow / onHide /onUnload` 5個回調是Page實例的生命周期函數;
生命周期的觸發以及頁面的路由方式[【詳見】](https://developers.weixin.qq.com/miniprogram/dev/framework/app-service/route.html)
* ***事件處理函數***
由用戶行為觸發的事件回調處理函數:`onPullDownRefresh / onReachBottom / onShareAppMessage / onPageScroll / onTabItemTap` 5個
* ***其他(用戶自定義函數/組件事件處理函數)***
Page 中還可以用戶自定義函數/組件事件處理函數。在渲染層的組件中加入事件綁定,當事件被觸發時,就會執行 Page 中定義的事件處理函數。
示例代碼:
~~~HTML
<!-- // *.wxml -->
<view bindtap="viewTap"> click me </view>
~~~
~~~JavaScript
// *.js
Page({
viewTap: function() {
console.log('view tap')
}
})
~~~
#### 2.2.2.3 頁面的生命周期和打開參數
:-: 
:-: Page 實例的生命周期圖
下述Page實例的5個生命周期函數/方法是由微信客戶端根據用戶操作主動觸發的。為了避免程序上的混亂,我們不應該在其他代碼中主動調用Page實例的生命周期函數。
**`onLoad(Object query)`**:頁面加載時觸發。一個頁面只會調用一次,可以在 onLoad 的參數中獲取打開當前頁面路徑中的參數(參數名稱:query,參數類型:Object)。
使用場景舉例:購物商城的小程序,一個商品列表頁和商品詳情頁,點擊商品列表頁的商品就可以跳轉到該商品的詳情頁。
當然我們不可能為每個商品單獨去實現它的詳情頁。我們只需要實現一個商品詳情頁的`pages/detail/detail`(代表WXML/WXSS/JS/JSON文件)即可,在列表頁打開商品詳情頁時把商品的id傳遞過來,詳情頁通過剛剛說的onLoad回調的參數option就可以拿到商品id,從而繪制出對應的商品,代碼如代碼清單3-9所示。
代碼清單3-9 Page構造器頁面的打開參數
~~~JavaScript
// pages/list/list.js
// 列表頁使用navigateTo跳轉到詳情頁
wx.navigateTo({ url: 'pages/detail/detail?id=1&other=abc' })
~~~
~~~JavaScript
// pages/detail/detail.js
Page({
onLoad: function(option) {
console.log(option.id)
console.log(option.other)
}
})
~~~
小程序把頁面的打開路徑定義成頁面URL,其組成格式和網頁的URL類似,在頁面路徑后使用英文` ?` 分隔path和query部分。
* query部分:多個參數使用 `&` 進行分隔,參數的名字和值使用 key=value 的形式聲明。
在頁面Page構造器里onLoad的option可以拿到當前頁面的打開參數,其類型是一個Object,其鍵值對與頁面URL上query鍵值對一一對應。
和網頁URL一樣,頁面URL上的value如果涉及特殊字符(例如:`&`字符、`?`字符、`中文`字符等,詳情參考URI的RFC3986說明 ),需要采用UrlEncode后再拼接到頁面URL上。
**`onShow()`**:頁面顯示/切入前臺時觸發。
**`onReady()`**:頁面初次渲染完成時觸發。一個頁面只會調用一次,代表頁面已經準備妥當,可以和視圖層進行交互。
>[warning] 對界面內容進行設置的 API 如wx.setNavigationBarTitle,請在onReady之后進行。
**`onHide()`**:頁面隱藏/切入后臺時觸發。 如 `navigateTo` 或底部 `tab` 切換到其他頁面,小程序切入后臺等。
**`onUnload()`** :頁面卸載時觸發。如`redirectTo`或`navigateBack`到其他頁面時。
* 頁面生命周期函數調用過程說明
頁面初次加載的時候,微信客戶端就會給Page實例派發onLoad事件,Page構造器參數所定義的`onLoad`方法會被調用,onLoad在頁面沒被銷毀之前只會觸發1次,在onLoad的回調中,可以獲取當前頁面所調用的打開參數query。
頁面顯示之后,Page構造器參數所定義的`onShow`方法會被調用,一般從別的頁面返回到當前頁面時,當前頁的onShow方法都會被調用。
在頁面初次渲染完成時,Page構造器參數所定義的`onReady`方法會被調用,onReady在頁面沒被銷毀前只會觸發1次,onReady觸發時,表示頁面已經準備妥當,在邏輯層就可以和視圖層進行交互了。
以上三個事件觸發的時機是`onLoad`早于` onShow`,`onShow`早于`onReady`。
頁面不可見時,Page構造器參數所定義的`onHide`方法會被調用,這種情況會在使用`wx.naviagteTo`切換到其他頁面、底部tab切換時觸發。
當前頁面使用`wx.redirectTo`或`wx.navigateBack`返回到其他頁時,當前頁面會被微信客戶端銷毀回收,此時Page構造器參數所定義的`onUnload`方法會被調用。
#### 2.2.2.4 頁面的數據
* `data`參數
小程序的頁面結構由對應的`*.wxml`文件進行描述,可以通過數據綁定的語法綁定從邏輯層傳遞過來的數據字段(頁面Page構造器的data參數中定義的字段),data參數是頁面第一次渲染時從邏輯層傳遞到渲染層的數據。
* `setData`函數
宿主環境所提供的Page實例的原型中有`setData`函數,我們可以在Page實例下的方法調用`this.setData`把數據傳遞給渲染層,從而達到更新界面的目的。
由于小程序的渲染層和邏輯層分別在兩個線程中運行,所以setData傳遞數據實際是一個異步的過程,所以setData的第二個參數是一個callback回調,在這次setData對界面渲染完畢后觸發。
setData其一般調用格式是 `setData(data, callback)`,其中`data`是由多個`key: value`構成的Object對象。
代碼清單3-11 使用setData更新渲染層數據
~~~JavaScript
// page.js
Page({
onLoad: function(){
this.setData({
text: 'change data'
}, function(){
// 在這次setData對界面渲染完畢后觸發
})
}
})
~~~
實際在開發的時候,頁面的data數據會涉及相當多的字段,只需要把改變的值進行設置即可,宿主環境會自動把新改動的字段合并到渲染層對應的字段中。`data`中的key還可以非常靈活,還可以數據路徑的形式給出,例如 `this.setData({"d[0]": 100})`; `this.setData({"d[1].text": 'Goodbye'})`;
>[warning] * 提高小程序的渲染性能的原則:`每次只設置需要改變的最小單位數據`。
> * 直接修改 Page實例的this.data 而不調用 this.setData 是無法改變頁面的狀態的,還會造成數據不一致。
> * 由于`setData`是需要兩個線程的一些通信消耗,為了提高性能,每次設置的數據不應超過`1024kB`。
> * 不要把`data`中的任意一項的value設為`undefined`,否則可能會有引起一些不可預料的bug。
代碼清單3-12 使用setData更新渲染層數據
~~~JavaScript
// page.js
Page({
data: {
a: 1, b: 2, c: 3,
d: [1, {text: 'Hello'}, 3, 4]
}
onLoad: function(){
// a需要變化時,只需要setData設置a字段即可
this.setData({a : 2})
}
})
~~~
#### 2.2.2.5 頁面的用戶行為
小程序宿主環境提供了5個和頁面相關的用戶行為回調:
1. **`onPullDownRefresh()`**:監聽用戶下拉刷新事件。
* 需要在app.json的window選項中或頁面配置中開啟`enablePullDownRefresh`。
* 可以通過`wx.startPullDownRefresh`觸發下拉刷新,調用后觸發下拉刷新動畫,效果與用戶手動下拉刷新一致。
* 當處理完數據刷新后,`wx.stopPullDownRefresh`可以停止當前頁面的下拉刷新。
2. **`onReachBottom()`**:監聽用戶上拉觸底事件。
* 可以在app.json的window選項中或頁面配置中設置觸發距離`onReachBottomDistance`。
* 在觸發距離內滑動期間,本事件只會被觸發一次。
3. **`onShareAppMessage(Object)`** :監聽用戶點擊頁面內轉發按鈕(`<button>` 組件 `open-type="share"`)或右上角菜單“轉發”按鈕的行為,并自定義轉發內
容。
>[warning] 只有定義了此事件處理函數,右上角菜單才會顯示“轉發”按鈕
Object 參數說明:
| 參數 | 類型 | 說明 | 最低版本 |
| ---| ---| --- | --- |
| from | String | 轉發事件來源。`button`:頁面內轉發按鈕;`menu`:右上角轉發菜單 | 1.2.4 |
| target | Object | 如果 from 值是 button,則 target 是觸發這次轉發事件的 button,否則為 undefined | 1.2.4 |
| webViewUrl | String | 頁面中包含<web-view>組件時,返回當前<web-view>的url | 1.6.4 |
此事件需要 return 一個 Object,用于自定義轉發內容,返回內容如下:
自定義轉發內容
| 字段 | 說明 | 默認值 | 最低版本 |
| ---| ---| --- | --- |
| title | 轉發標題 | 當前小程序名稱 | |
| path | 轉發路徑 | 當前頁面 path ,必須是以 / 開頭的完整路徑 | |
| imageUrl | 自定義圖片路徑,可以是本地文件路徑、代碼包文件路徑或者網絡圖片路徑。支持PNG及JPG。顯示圖片長寬比是 5:4。 | 使用默認截圖 |
1.5.0 |
示例代碼
~~~JavaScript
Page({
onShareAppMessage: function (res) {
if (res.from === 'button') {
// 來自頁面內轉發按鈕
console.log(res.target)
}
return {
title: '自定義轉發標題',
path: '/page/user?id=123'
}
}
})
~~~
4. **`onPageScroll(Object)`**:監聽用戶滑動頁面事件。
Object 參數說明:
| 屬性 | 類型 | 說明 |
| ---| ---| --- |
| scrollTop | Number | 頁面在垂直方向已滾動的距離(單位px) |
5. **`onTabItemTap(Object)`**:
> 基礎庫 1.9.0 開始支持,低版本需做兼容處理
點擊 tab 時觸發
Object 參數說明:
| 參數 | 類型 | 說明 | 最低版本 |
| ---| ---| --- | --- |
| index | String | 被點擊tabItem的序號,從0開始 | 1.9.0 |
| pagePath | String | 被點擊tabItem的頁面路徑 | 1.9.0 |
| text | String | 被點擊tabItem的按鈕文字 | 1.9.0 |
示例代碼:
~~~JavaScript
Page({
onTabItemTap(item) {
console.log(item.index)
console.log(item.pagePath)
console.log(item.text)
}
})
~~~
* 用戶自定義函數/組件事件處理函數
示例代碼
~~~HTML
<!--index.wxml-->
<view>{{text}}</view>
<button bindtap="changeText"> Change normal data </button>
<view>{{num}}</view>
<button bindtap="changeNum"> Change normal num </button>
<view>{{array[0].text}}</view>
<button bindtap="changeItemInArray"> Change Array data </button>
<view>{{object.text}}</view>
<button bindtap="changeItemInObject"> Change Object data </button>
<view>{{newField.text}}</view>
<button bindtap="addNewField"> Add new data </button>
~~~
~~~JavaScript
//index.js
Page({
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'
})
}
})
~~~
#### 2.2.2.6 頁面路由和跳轉
在小程序中所有頁面的路由全部由框架進行管理。
1. **頁面棧**
一個小程序擁有多個頁面,我們可以通過`wx.navigateTo`推入一個新的頁面。在首頁使用2次`wx.navigateTo`后,頁面層級會有三層,我們把這樣的一個頁面層級稱為`頁面棧`。
頁面棧描述:\[ pageA, pageB, pageC ],其中pageA在最底下,pageC在最頂上,也就是用戶所看到的界面。小程序宿主環境限制了這個頁面棧的最大層級為10層 ,也就是當頁面棧到達10層之后就沒有辦法再推入新的頁面了。
當發生路由切換的時候,頁面棧的表現如下:
| 路由方式 | 頁面棧表現 |
| --- | --- |
| 初始化 | 新頁面入棧 |
| 打開新頁面 | 新頁面入棧 | |
| 頁面重定向 | 當前頁面出棧,新頁面入棧 |
| 頁面返回 | 頁面不斷出棧,直到目標返回頁 |
| Tab 切換 | 頁面全部出棧,只留下新的 Tab 頁面 |
| 重加載 | 頁面全部出棧,只留下新的頁面 |
2. **getCurrentPages()**
`getCurrentPages()` 函數用于獲取當前頁面棧的實例,以數組形式按棧的順序給出,第一個元素為首頁,最后一個元素為當前頁面。
>[danger] 不要嘗試修改頁面棧,會導致路由以及頁面狀態錯誤。
3. **路由方式**
對于路由的觸發方式以及頁面生命周期函數如下:
| 路由方式 | 觸發時機 | 路由前頁面 | 路由后頁面 |
| --- | --- | --- |---|
| 初始化 | 小程序打開的第一個頁面 | | onLoad, onShow |
| 打開新頁面 | 調用 API `wx.navigateTo` 或使用組件 `<navigator open-type="navigateTo"/>` | onHide | onLoad, onShow |
| 頁面重定向 | 調用 API `wx.redirectTo` 或使用組件 `<navigator open-type="redirectTo"/>` | onUnload | onLoad, onShow |
| 頁面返回 | 調用 API `wx.navigateBack` 或使用組件`<navigator open-type="navigateBack">`或用戶按左上角返回按鈕 | onUnload | onShow |
| Tab 切換 | 調用 API `wx.switchTab` 或使用組件 `<navigator open-type="switchTab"/>` 或用戶切換 Tab | | 各種情況請參考下表 |
| 重啟動 | 調用 API `wx.reLaunch` 或使用組件`<navigator open-type="reLaunch"/>` | onUnload | onLoad, onShow |
Tab 切換對應的生命周期(以 A、B 頁面為 Tabbar 頁面,C 是從 A 頁面打開的頁面,D 頁面是從 C 頁面打開的頁面為例):
| 當前頁面 | 路由后頁面 | 觸發的生命周期(按順序)|
| --- | --- | --- |
| A | A | 無 |
| A | B | A.onHide(), B.onLoad(), B.onShow() |
| A | B(再次打開) | A.onHide(), B.onShow() |
| C | A | C.onUnload(), A.onShow() |
| C | B | C.onUnload(), B.onLoad(), B.onShow() |
| D | B | D.onUnload(), C.onUnload(), B.onLoad(), B.onShow() |
| D(從轉發進入) | A | D.onUnload(), A.onLoad(), A.onShow() |
| D(從轉發進入) | B | D.onUnload(), B.onLoad(), B.onShow() |
4. **和路由相關的API**
* `wx.navigateTo({ url: 'pageD' })`: 可以往當前頁面棧多推入一個 pageD,此時頁面棧變成 \[ pageA, pageB, pageC, pageD ]。只能打開非TabBar頁面。
* `wx.navigateBack()` 可以退出當前頁面棧的最頂上頁面,此時頁面棧變成 \[ pageA, pageB, pageC ]。
* `wx.redirectTo({ url: 'pageE' })` 是替換當前頁變成pageE,此時頁面棧變成 \[ pageA, pageB, pageE ],當頁面棧到達10層沒法再新增的時候,往往就是使用redirectTo這個API進行頁面跳轉。只能打開非TabBar頁面。
* `wx.switchTab`。頁面棧中使用`wx.switchTab({ url: 'pageF' })`,此時原來的頁面棧會被清空(除了已經聲明為Tabbar頁的pageA外其他頁面會被銷毀),然后會切到pageF所在的tab頁面,頁面棧變成 \[ pageF ],此時點擊Tab1切回到pageA時,pageA不會再觸發onLoad,因為pageA沒有被銷毀。
* `wx.reLaunch`。我們還可以使用`wx. reLaunch({ url: 'pageH' })` 重啟小程序,并且打開pageH,此時頁面棧為 \[ pageH ]。
小程序提供了原生的`Tabbar`支持,我們可以在`app.json`聲明tabBar字段來定義Tabbar頁(注:更多詳細參數見Tabbar官方文檔 )。
代碼清單3-14 app.json定義小程序底部tab
~~~JavaScript
{
"tabBar": {
"list": [
{ "text": "Tab1", "pagePath": "pageA" },
{ "text": "Tab1", "pagePath": "pageF" },
{ "text": "Tab1", "pagePath": "pageG" }
]
}
}
~~~
>[warning] * `wx.navigateTo`, `wx.redirectTo` 只能打開非 tabBar 頁面。
> * `wx.switchTab` 只能打開 tabBar 頁面。
> * `wx.reLaunch` 可以打開任意頁面。
> * 頁面底部的 `tabBar` 由頁面決定,即只要是定義為 tabBar 的頁面,底部都有 tabBar。
> * 調用頁面路由帶的參數可以在目標頁面的`onLoad`中獲取。
5. **路由信息 `Page.prototype.route`**
>基礎庫 1.2.0 開始支持,低版本需做兼容處理
route 字段可以獲取到當前頁面的路徑。
~~~JavaScript
//index.js
Page({
onShow:function(){
console.log(this.route)
}
})
~~~
- 微信
- 小程序
- 1. 代碼組成
- 1.1 JSON配置--'*.json'文件
- 1.2 WXML模板--'*.wxml'文件
- 1.3 WXSS樣式--'*.wxss'文件
- 1.4 JavaScript腳本--'*.js'文件
- 2. 客戶端運行
- 2.1 邏輯層和渲染層
- 2.1.1 邏輯層--App Service
- 2.1.2 渲染層/視圖層--View
- 2.1.3 通信模型
- 2.1.4 數據驅動
- 2.1.5 雙線程下的界面渲染
- 2.2 程序與頁面
- 2.3 組件
- 2.4 API
- 2.5 事件
- 2.6 兼容
- 3. 應用設計
- 3.1 Flex布局
- 3.2 界面常見的交互反饋
- 3.3 發起HTTPS網絡通信--wx.request
- 3.4 微信登錄
- 3.5 本地數據緩存
- 3.6 設備能力
- 4. 小程序的協同工作和發布
- 4.1 協同工作
- 4.2 用戶體驗審視
- 4.3 發布
- 4.4 運營
- 5. 底層框架
- 5.1 雙線程模型
- 5.2 組件系統--Exparser框架
- 5.3 原生組件
- 5.4 小程序與客戶端通信原理
- 6. 運行和性能優化
- 6.1 啟動--代碼加載
- 6.2 頁面準備
- 6.3 數據通信
- 6.4 視圖層渲染
- 6.5 原生組件通信
- 7. 小程序基礎庫的更新迭代
- 8. 微信開發者工具
- 騰訊云支持
- wafer
- Wafer2 快速開發 Demo - PHP
- WXAPI
- api列表