微信小程序開發之官方文檔學習(一)

小程序宿主環境

渲染層和邏輯層

  小程序的運行環境分紅渲染層和邏輯層:WXML 模板和 WXSS 樣式工做在渲染層,JS 腳本工做在邏輯層。渲染層和數據相關;邏輯層負責產生、處理數據,經過 Page 實例的 data 屬性傳遞數據到渲染層。php

通訊模型

  小程序的渲染層和邏輯層分別由2個線程管理:渲染層的界面使用了WebView 進行渲染;邏輯層採用JsCore線程運行JS腳本。一個小程序存在多個界面,因此渲染層存在多個WebView線程,這兩個線程的通訊會經由微信客戶端(下文中也會採用Native來代指微信客戶端)作中轉,邏輯層發送網絡請求也經由Native轉發,小程序的通訊模型以下圖所示:html

小程序的通訊模型

程序與頁面

  從邏輯組成來講,一個小程序是由多個「頁面」組成的「程序」。ios

程序

  「小程序」指的是產品層面的程序,而「程序」指的是代碼層面的程序實例。json

程序構造器App()

  宿主環境提供了 App() 構造器用來註冊一個程序App。App() 構造器必須寫在項目根目錄的app.js裏,App實例是單例對象,在其餘JS腳本中可使用宿主環境提供的 getApp() 來獲取程序實例。小程序

1 // other.js
2 var appInstance = getApp()

  App() 的調用方式如上所示,App構造器接受一個Object參數,參數說明以下:api

1 App({
2   onLaunch: function(options) {},
3   onShow: function(options) {},
4   onHide: function() {},
5   onError: function(msg) {},
6   globalData: 'I am global data'
7 })

  其中onLaunch / onShow / onHide 三個回調是App實例的生命週期函數;當小程序發生腳本錯誤,或者 API 調用失敗時,會觸發 onError 方法並帶上錯誤信息;globalData 用於存放App實例的數據,能夠添加任意的函數或數據到 Object 參數中,在App實例回調用 this 能夠訪問。數組

程序的生命週期和打開場景

  • 初次進入小程序的時候,微信客戶端初始化好宿主環境,同時從網絡下載或者從本地緩存中拿到小程序的代碼包,把它注入到宿主環境,初始化完畢後,微信客戶端就會給App實例派發onLaunch事件,App構造器參數所定義的onLaunch方法會被調用
  • 進入小程序以後,用戶能夠點擊右上角的關閉,或者按手機設備的Home鍵離開小程序,此時小程序並沒有被直接銷燬,咱們把這種狀況稱爲「小程序進入後臺狀態」,App構造器參數所定義的onHide方法會被調用
  • 當再次回到微信或者再次打開小程序時,微信客戶端會把「後臺」的小程序喚醒,咱們把這種狀況稱爲「小程序進入前臺狀態」,App構造器參數所定義的onShow方法會被調用

  爲了不程序上的混亂,咱們不該該從其餘代碼裏主動調用App實例的生命週期函數。
  在微信客戶端中打開小程序有不少途徑:從羣聊會話裏打開,從小程序列表中打開,經過微信掃一掃二維碼打開,從另一個小程序打開當前小程序等,針對不一樣途徑的打開方式,小程序有時須要作不一樣的業務處理,因此微信客戶端會把打開方式帶給onLaunch和onShow的調用參數options。要獲取最新的場景值說明請查看官方文檔:https://mp.weixin.qq.com/debug/wxadoc/dev/framework/app-service/app.html緩存

1 App({
2   onLaunch: function(options) { console.log(options) },
3   onShow: function(options) { console.log(options) }
4 })

  options屬性說明以下:微信

屬性 類型 說明
path String 打開小程序的頁面路徑
query Object 打開小程序的頁面參數query
scene Number 打開小程序的場景值,詳細場景值請參考小程序官方文檔
shareTicket String shareTicket,詳見小程序官方文檔
referrerInfo Object 當場景爲由從另外一個小程序或公衆號或App打開時,返回此字段
referrerInfo.appId String 來源小程序或公衆號或App的 appId,詳見下方說明
referrerInfo.extraData Object 來源小程序傳過來的數據,scene=1037或1038時支持

全局數據

  前邊咱們提到,小程序的JS腳本是運行在JsCore的線程裏,小程序的每一個頁面各自有一個WebView線程進行渲染,因此小程序切換頁面時,小程序邏輯層的JS腳本運行上下文依舊在同一個JsCore線程中。同時App實例又是單例的,所以不一樣頁面直接能夠經過App實例下的屬性來共享數據。網絡

1 // app.js
2 App({
3   globalData: 'I am global data' // 全局共享數據
4 })
5 // 其餘頁面腳本other.js
6 var appInstance = getApp()
7 console.log(appInstance.globalData) // 輸出: I am global data

  注意:全部頁面的腳本邏輯都跑在同一個JsCore線程,頁面使用setTimeout或者setInterval的定時器,而後跳轉到其餘頁面時,這些定時器並無被清除,須要開發者本身在頁面離開的時候進行清理。

頁面

  一個小程序能夠有不少頁面,每一個頁面承載不一樣的功能,頁面之間能夠互相跳轉。

文件構成和路徑

  一個頁面是分三部分組成:界面、配置和邏輯。界面由WXML文件和WXSS文件來負責描述,配置由JSON文件進行描述,頁面邏輯則是由JS腳本文件負責。一個頁面的文件須要放置在同一個目錄下,其中WXML文件和JS文件是必須存在的,JSON和WXSS文件是可選的

  頁面路徑須要在小程序代碼根目錄app.json中的pages字段聲明,不然這個頁面不會被註冊到宿主環境中。腳本中的路徑採用相對路徑,app.json的pages字段的代碼路徑以pages文件夾開始。

1 {
2   "pages":[
3     "pages/index/page", // 第一項默認爲首頁
4     "pages/other/other"
5   ]
6 }

頁面構造器Page()

  宿主環境提供了 Page() 構造器用來註冊一個小程序頁面,Page()在頁面腳本page.js中調用。Page構造器接受一個Object參數,參數說明以下:

 1 Page({
 2   data: { text: "This is page data." },
 3   onLoad: function(options) { },
 4   onReady: function() { },
 5   onShow: function() { },
 6   onHide: function() { },
 7   onUnload: function() { },
 8   onPullDownRefresh: function() { },
 9   onReachBottom: function() { },
10   onShareAppMessage: function () { },
11   onPageScroll: function() { }
12 })

  其中data屬性是當前頁面WXML模板中能夠用來作數據綁定的初始數據;onLoad / onReady / onShow / onHide /onUnload 5個回調是Page實例的生命週期函數;onPullDownRefresh / onReachBottom / onShareAppMessage / onPageScroll 4個回調是頁面的用戶行爲。具體說明以下表:

參數屬性 類型 描述
data Object 頁面的初始數據
onLoad Function 生命週期函數--監聽頁面加載,觸發時機早於onShow和onReady
onReady Function 生命週期函數--監聽頁面初次渲染完成
onShow Function 生命週期函數--監聽頁面顯示,觸發事件早於onReady
onHide Function 生命週期函數--監聽頁面隱藏
onUnload Function 生命週期函數--監聽頁面卸載
onPullDownRefresh Function 頁面相關事件處理函數--監聽用戶下拉動做
onReachBottom Function 頁面上拉觸底事件的處理函數
onShareAppMessage Function 用戶點擊右上角轉發
onPageScroll Function 頁面滾動觸發事件的處理函數
其餘 Any 能夠添加任意的函數或數據,在Page實例的其餘函數中用 this 能夠訪問

頁面的生命週期和打開參數

  • 頁面初次加載的時候,微信客戶端就會給Page實例派發onLoad事件,Page構造器參數所定義的onLoad方法會被調用,onLoad在頁面沒被銷燬以前只會觸發1次在onLoad的回調中,能夠獲取當前頁面所調用的打開參數option
  • 頁面顯示以後,Page構造器參數所定義的onShow方法會被調用,通常從別的頁面返回到當前頁面時,當前頁的onShow方法也會被調用
  • 頁面初次渲染完成時,Page構造器參數所定義的onReady方法會被調用onReady在頁面沒被銷燬前只會觸發1次onReady觸發時,表示頁面已經準備穩當,在邏輯層就能夠和視圖層進行交互了。

  以上三個事件觸發的時機是onLoad早於 onShowonShow早於onReady

  • 頁面不可見時,Page構造器參數所定義的onHide方法會被調用,這種狀況會在使用wx.navigateTo切換到其餘頁面、底部tab切換時觸發
  • 當前頁面使用wx.redirectTo或wx.navigateBack返回到其餘頁時當前頁面會被微信客戶端銷燬回收,此時Page構造器參數所定義的onUnload方法會被調用

頁面的打開參數query

  用於傳遞上一個頁面傳遞到下一個頁面的參數

 1 // pages/list/list.js
 2 // 列表頁使用navigateTo跳轉到詳情頁
 3 wx.navigateTo({ url: 'pages/detail/detail?id=1&other=abc' })
 4 
 5 // pages/detail/detail.js
 6 Page({
 7   onLoad: function(option) {
 8         console.log(option.id)
 9         console.log(option.other)
10   }
11 })

  頁面的打開路徑定義被定義爲頁面URL,其組成格式和網頁的URL相似,在頁面路徑後使用英文 ? 分隔path和query部分,query部分的多個參數使用 & 進行分隔,參數的名字和值使用 key=value 的形式聲明。在頁面Page構造器裏onLoad的option能夠拿到當前頁面的打開參數,其類型是一個Object,其鍵值對與頁面URL上query鍵值對一一對應。和網頁URL同樣,頁面URL上的value若是涉及特殊字符(例如:&字符、?字符、中文字符等,詳情參考URI的RFC3986說明 ),須要採用UrlEncode後再拼接到頁面URL上。

頁面的數據

  WXML能夠經過數據綁定的語法綁定從邏輯層傳遞過來的數據字段,來自於頁面Page構造器的data字段,data參數是頁面第一次渲染時從邏輯層傳遞到渲染層的數據。

  宿主環境所提供的Page實例的原型中有setData函數,咱們能夠在Page實例下的方法調用this.setData把數據傳遞給渲染層,從而達到更新界面的目的。因爲小程序的渲染層和邏輯層分別在兩個線程中運行,因此setData傳遞數據實際是一個異步的過程,因此setData的第二個參數是一個callback回調,在此次setData對界面渲染完畢後觸發。setData其通常調用格式是 setData(data, callback),其中data是由多個key: value構成的Object對象。

 1 // page.js
 2 Page({
 3   onLoad: function(){
 4     this.setData({
 5       text: 'change data'
 6     }, function(){
 7       // 在此次setData對界面渲染完畢後觸發
 8     })
 9   }
10 })

  實際開發中,一般只須要設置須要改變的屬性便可。

 1 // page.js
 2 Page({
 3   data: {
 4     a: 1, b: 2, c: 3,
 5     d: [1, {text: 'Hello'}, 3, 4]
 6   }
 7   onLoad: function(){
 8        // a須要變化時,只須要setData設置a字段便可
 9     this.setData({a : 2})
10   }
11 })
  • 直接修改 Page實例的this.data 而不調用 this.setData 是沒法改變頁面的狀態的,還會形成數據不一致;
  • 因爲setData是須要兩個線程的一些通訊消耗,爲了提升性能,每次設置的數據不該超過1024kB;
  • 不要把data中的任意一項的value設爲undefined,不然可能會有引發一些不可預料的bug;

頁面的用戶行爲

  宿主環境提供了四個和頁面相關的用戶行爲回調:

    1. 下拉刷新 onPullDownRefresh
      監聽用戶下拉刷新事件,須要在app.json的window選項中或頁面配置page.json中設置enablePullDownRefresh爲true。當處理完數據刷新後,wx.stopPullDownRefresh能夠中止當前頁面的下拉刷新。
    2. 上拉觸底 onReachBottom
      監聽用戶上拉觸底事件。能夠在app.json的window選項中或頁面配置page.json中設置觸發距離onReachBottomDistance。在觸發距離內滑動期間,本事件只會被觸發一次。
    3. 頁面滾動 onPageScroll
      監聽用戶滑動頁面事件,參數爲 Object,包含 scrollTop 字段,表示頁面在垂直方向已滾動的距離(單位px)。
    4. 用戶轉發 onShareAppMessage
      只有定義了此事件處理函數,右上角菜單纔會顯示「轉發」按鈕,在用戶點擊轉發按鈕的時候會調用,此事件須要return一個Object,包含title和path兩個字段,用於自定義轉發內容。

 

 

頁面跳轉和路由

  一個小程序擁有多個頁面,咱們能夠經過wx.navigateTo推入一個新的頁面。在首頁使用2次wx.navigateTo後,頁面層級會有三層,咱們把這樣的一個頁面層級稱爲頁面棧。以下圖所示:

使用2次wx.navigateTo後的頁面棧

  爲了表述方便,咱們採用這樣的方式進行描述頁面棧:[ pageA, pageB, pageC ],其中pageA在最底下,pageC在最頂上。宿主環境限制了頁面棧的最大層級爲10層,也就是當頁面棧到達10層以後就沒有辦法再推入新的頁面了。

  使用 wx.navigateTo({ url: 'pageD' }) 能夠往當前頁面棧多推入一個 pageD,此時頁面棧變成 [ pageA, pageB, pageC, pageD ]。
  使用 wx.navigateBack() 能夠退出當前頁面棧的最頂上頁面,此時頁面棧變成 [ pageA, pageB, pageC ]。
  使用wx.redirectTo({ url: 'pageE' }) 是替換當前頁變成pageE,此時頁面棧變成 [ pageA, pageB, pageE ],當頁面棧到達10層無法再新增的時候,每每就是使用redirectTo這個API進行頁面跳轉

  小程序提供了原生的Tabbar支持,咱們能夠在app.json聲明tabBar字段來定義Tabbar頁(注:更多詳細參數見Tabbar官方文檔 )。

1 {
2   "tabBar": {
3     "list": [
4       { "text": "Tab1", "pagePath": "pageA" },
5       { "text": "Tab1", "pagePath": "pageF" },
6       { "text": "Tab1", "pagePath": "pageG" }
7     ]
8   }
9 }

  在剛剛的例子所在的頁面棧中使用wx.switchTab({ url: 'pageF' }),此時原來的頁面棧會被清空(除了已經聲明爲Tabbar頁pageA外其餘頁面會被銷燬),而後會切到pageF所在的tab頁面,頁面棧變成 [ pageF ],此時點擊Tab1切回到pageA時,pageA不會再觸發onLoad,由於pageA沒有被銷燬。

   wx.navigateTo和wx.redirectTo只能打開非TabBar頁面,wx.switchTab只能打開Tabbar頁面。咱們還可使用 wx. reLaunch({ url: 'pageH' }) 重啓小程序,而且打開pageH,此時頁面棧爲 [ pageH ]。

路由方式 觸發時機 路由前頁面生命週期 路由後頁面生命週期
初始化 小程序打開的第一個頁面   onLoad, onShow
打開新頁面 調用 API wx.navigateTo onHide onLoad, onShow
頁面重定向 調用 API wx.redirectTo onUnload onLoad, onShow
頁面返回 調用 API wx.navigateBack onUnload onShow
Tab 切換 調用 API wx.switchTab 請參考表3-6 請參考表3-6
重啓動 調用 API wx.reLaunch onUnload onLoad, onShow

組件

  組件就是小程序頁面的基本組成單元。組件是在WXML模板文件聲明中使用的,WXML的語法和HTML語法類似,小程序使用標籤名來引用一個組件,一般包含開始標籤和結束標籤,該標籤的屬性用來描述該組件。

1 <!-- page.wxml -->
2 <image mode="scaleToFill" src="img.png"></image>

  全部組件都擁有下表列舉的屬性:

組件共有屬性
屬性名 類型 描述 其餘說明
id String 組件的惟一標示 保持整個頁面惟一
class String 組件的樣式類 在對應的WXSS中定義的樣式類
style String 組件的內聯樣式 能夠經過數據綁定進行動態設置的內聯樣式
hidden Boolean 組件是否顯示 全部組件默認顯示
data-* Any 自定義屬性 組件上觸發的事件時,會發送給事件處理函數
bind / catch EventHandler 事件 詳情見3.5節

  更多相關屬性,請在使用時前往官方文檔進行查閱相關組件說明 https://mp.weixin.qq.com/debug/wxadoc/dev/component/

官方API

  幾乎全部小程序的API都掛載在wx對象(小程序的宿主環境所提供的全局對象)底下(除了Page/App等特殊的構造器),因此本書談到API概念時,一般指的是wx對象底下的方法。

  小程序提供的API按照功能主要分爲幾大類:網絡、媒體、文件、數據緩存、位置、設備、界面、界面節點信息還有一些特殊的開放接口,咱們介紹一下API通常調用的約定:

  1. wx.on* 開頭的 API 是監聽某個事件發生的API接口,接受一個 Callback 函數做爲參數。當該事件觸發時,會調用 Callback 函數。
  2. 如未特殊約定,多數 API 接口爲異步接口 ,都接受一個Object做爲參數。
  3. API的Object參數通常由success、fail、complete三個回調來接收接口調用結果,示例代碼如代碼清單3-17所示,詳細說明如表3-9所示。
  4. wx.get* 開頭的API是獲取宿主環境數據的接口。
  5. wx.set* 開頭的API是寫入數據到宿主環境的接口。
 1 wx.request({
 2 url: 'test.php',
 3 data: {},
 4 header: { 'content-type': 'application/json' },
 5 success: function(res) {
 6  // 收到https服務成功後返回
 7  console.log(res.data)
 8 },
 9 fail: function() {
10  // 發生網絡錯誤等狀況觸發
11 },
12 complete: function() {
13  // 成功或者失敗後觸發
14 }
15 })

  能夠在官方API文檔 https://mp.weixin.qq.com/debug/wxadoc/dev/api/瞭解到對應的API參數細節。

事件

  咱們把「用戶在渲染層的行爲反饋」以及「組件的部分狀態反饋」抽象爲渲染層傳遞給邏輯層的「事件」。

渲染層產生用戶交互事件傳遞給邏輯層

事件類型和事件對象

  常見的事件類型:

類型 觸發條件
touchstart 手指觸摸動做開始
touchmove 手指觸摸後移動
touchcancel 手指觸摸動做被打斷,如來電提醒,彈窗
touchend 手指觸摸動做結束
tap 手指觸摸後立刻離開
longpress 手指觸摸後,超過350ms再離開,若是指定了事件回調函數並觸發了這個事件,tap事件將不被觸發
longtap 手指觸摸後,超過350ms再離開(推薦使用longpress事件代替)
transitionend 會在 WXSS transition 或 wx.createAnimation 動畫結束後觸發
animationstart 會在一個 WXSS animation 動畫開始時觸發
animationiteration 會在一個 WXSS animation 一次迭代結束時觸發
animationend 會在一個 WXSS animation 動畫完成時觸發

 

  當事件回調觸發的時候,會收到一個事件對象,對象的詳細屬性以下表所示:

屬性 類型 說明
type String 事件類型
timeStamp Integer 頁面打開到觸發事件所通過的毫秒數
target Object 觸發事件的組件的一些屬性值集合
currentTarget Object 當前組件的一些屬性值集合
detail Object 額外的信息
touches Array 觸摸事件,當前停留在屏幕中的觸摸點信息的數組
changedTouches Array 觸摸事件,當前變化的觸摸點信息的數組

事件綁定與冒泡捕獲

 

事件綁定的寫法和組件屬性一致,以key="value"的形式,其中:

  • key以bind或者catch開頭,而後跟上事件的類型,如bindtap、catchtouchstart。自基礎庫版本1.5.0起,bind和catch後能夠緊跟一個冒號,其含義不變,如bind:tap、catch:touchstart。同時bind和catch前還能夠加上capture-來表示捕獲階段。
  • value是一個字符串,須要在對應的頁面Page構造器中定義同名的函數,不然觸發事件時在控制檯會有報錯信息。
  • bind和capture-bind的含義分別表明事件的冒泡階段和捕獲階段

 

  如下示例中,點擊 inner view 會前後調用handleTap一、handleTap二、handleTap三、handleTap4。

1 <view id="outer" bind:tap="handleTap4" capture-bind:tap="handleTap1">
2   outer view
3   <view id="inner" bind:tap="handleTap3" capture-bind:tap="handleTap2">
4     inner view
5   </view>
6 </view>

  bind事件綁定不會阻止冒泡事件向上冒泡,catch事件綁定能夠阻止冒泡事件向上冒泡。若是將以上代碼的capture-bind:tap="handleTap1"改爲capture-catch:tap="handleTap1",點擊inner view只會觸發handleTap1(catch事件阻止了tap事件冒泡)。

1 <view id="outer" bind:tap="handleTap4" capture-catch:tap="handleTap1">
2   outer view
3   <view id="inner" bind:tap="handleTap3" capture-bind:tap="handleTap2">
4     inner view
5   </view>
6 </view>

注意:除前邊表中列舉的事件類型以外的其餘組件自定義事件,如無特殊聲明都是非冒泡事件,如<form/>的submit事件,<input/>的input事件,<scroll-view/>的scroll事件。

兼容

  針對不一樣手機進行程序上的兼容,此時可使用 wx.getSystemInfo 或者 wx.getSystemInfoSync 來獲取手機品牌、操做系統版本號、微信版本號以及小程序基礎庫版本號等,經過這個信息,咱們能夠針對不一樣平臺作差別化的服務。

  能夠經過wx.getSystemInfoSync獲取宿主環境信息:

 1 wx.getSystemInfoSync()
 2 /*
 3   {
 4     brand: "iPhone",      // 手機品牌
 5     model: "iPhone 6",    // 手機型號
 6     platform: "ios",      // 客戶端平臺
 7     system: "iOS 9.3.4",  // 操做系統版本
 8     version: "6.5.23",    // 微信版本號
 9     SDKVersion: "1.7.0",  // 小程序基礎庫版本
10     language: "zh_CN",    // 微信設置的語言
11     pixelRatio: 2,        // 設備像素比
12     screenWidth: 667,    // 屏幕寬度
13     screenHeight: 375,     // 屏幕高度
14     windowWidth: 667,    // 可以使用窗口寬度
15     windowHeight: 375,     // 可以使用窗口高度
16     fontSizeSetting: 16   // 用戶字體大小設置
17   }
18  */

  經過判斷API是否存在作兼容:

1 if (wx.openBluetoothAdapter) {
2   wx.openBluetoothAdapter()
3 } else {
4   // 若是但願用戶在最新版本的客戶端上體驗您的小程序,能夠這樣子提示
5   wx.showModal({
6     title: '提示',
7     content: '當前微信版本太低,沒法使用該功能,請升級到最新微信版本後重試。'
8   })
9 }

小程序還提供了wx.canIUse這個API,用於判斷接口或者組件在當前宿主環境是否可用,其參數格式爲: ${API}.${method}.${param}.${options}或者${component}.${attribute}.${option}
各個段的含義以下:

  • ${API} 表明 API 名字
  • ${method} 表明調用方式,有效值爲return, success, object, callback
  • ${param} 表明參數或者返回值
  • ${options} 表明參數的可選值
  • ${component} 表明組件名字
  • ${attribute} 表明組件屬性
  • ${option} 表明組件屬性的可選值
 1 // 判斷接口及其參數在宿主環境是否可用
 2 wx.canIUse('openBluetoothAdapter')
 3 wx.canIUse('getSystemInfoSync.return.screenWidth')
 4 wx.canIUse('getSystemInfo.success.screenWidth')
 5 wx.canIUse('showToast.object.image')
 6 wx.canIUse('onCompassChange.callback.direction')
 7 wx.canIUse('request.object.method.GET')
 8 
 9  // 判斷組件及其屬性在宿主環境是否可用
10 wx.canIUse('contact-button')
11 wx.canIUse('text.selectable')
12 wx.canIUse('button.open-type.contact')

  咱們能夠選擇合適的判斷方法來作小程序的向前兼容,以保證咱們的小程序在舊版本的微信客戶端也能工做正常。在不得已的狀況下(小程序強依賴某個新的API或者組件時),還能夠經過在小程序管理後臺設置「基礎庫最低版本設置」來達到不向前兼容的目的。例如你選擇設置你的小程序只支持1.5.0版本以上的宿主環境,那麼當運行着1.4.0版本宿主環境的微信用戶打開你的小程序的時候,微信客戶端會顯示當前小程序不可用,而且提示用戶應該去升級微信客戶端。

相關文章
相關標籤/搜索