小程序05 深刻小程序框架

MINA框架

概述

以前介紹的WXML、WXSS、JS、WXS都是運行在MINA框架上。html

MINA的核心是一個響應的數據綁定系統。整個系統分爲兩塊:視圖層(View) 和 邏輯層(App Service)。MINA可讓數據與視圖保持同步很是簡單。當作數據修改的時候,只須要在邏輯層修改數據,視圖層就會作相應的更新。json

 

框架實現

框架原理圖以下:小程序

 

 

View視圖層:wxml是MINA提供的一套相似html標籤的語言以及一系列基礎組件。開發者使用wxml文件來搭建頁面的基礎視圖結構,使用wxss文件來控制頁面的展示樣式。緩存

AppService應用邏輯層:MINA的服務中心,由微信客戶端啓用異步線程單獨加載運行。頁面渲染所需的數據、頁面交互處理邏輯都在AppService中實現。MINA框架中的AppService使用JavaScript來編寫交互邏輯、網絡請求、數據處理,但不能使用JavaScript中的DOM操做。小程序中的各個頁面能夠經過AppService實現數據管理、網絡通訊、應用生命週期管理和頁面路由。Mananger用於處理事件邏輯和事件處理,API向外提供了應用接口。服務器

MINA框架爲頁面組件提供了bindtap、bindtouchstart等事件監聽相關的屬性,來與AppService中的事件處理函數綁定在一塊兒,實現也面向AppService層同步用戶交互數據。MINA框架同時提供了不少方法將AppService中的數據與頁面進行單向綁定,當AppService中的數據變動時,會主動觸發對應頁面組件的從新渲染。MINA使用virtualdom技術,加快了頁面的渲染效率。微信

 

運行機制

啓動

分爲熱啓動和冷啓動。網絡

  • 冷啓動:用戶第一次訪問小程序,須要到服務器加載小程序代碼包。或者用戶訪問已經被微信主動銷燬的小程序。
  • 微信銷燬小程序時機:小程序進入後臺後5分鐘;或者小程序在連續兩次間隔5秒發生告警。
  • 熱啓動:假如用戶已經打開過某小程序,而後在必定時間內再次打開該小程序,此時無需從新啓動,只需將後臺態的小程序切換到前臺,這個過程就是熱啓動。
  • 小程序的更新機制:小程序冷啓動時若是發現有新版本,將會異步下載新版本的代碼包,並同時用客戶端本地的包進行啓動,即新版本的小程序須要等下一次冷啓動纔會應用上。

加載

小程序啓動的時候會從CDN服務器上加載小程序代碼包,並緩存到微信後臺,下載完成後啓動小程序。若是本地已經緩存小程序代碼包,每次啓動小程序,微信會從CDN上查詢是否有新版本的代碼包,若是有,則在後臺下載,下次啓動的時候啓用新版小程序。併發

小程序經過AJAX嚮應用服務器發起請求,應用服務器返回JSON格式的數據。app

 

生命週期

應用生命週期

App() 函數用來註冊一個小程序。接受一個 object 參數,其指定小程序的生命週期函數等。object參數說明:框架

屬性

類型

描述

觸發時機

onLaunch

Function

生命週期函數--監聽小程序初始化

當小程序初始化完成時,會觸發 onLaunch(全局只觸發一次)

onShow

Function

生命週期函數--監聽小程序顯示

當小程序啓動,或從後臺進入前臺顯示,會觸發 onShow

onHide

Function

生命週期函數--監聽小程序隱藏

當小程序從前臺進入後臺,會觸發 onHide

onError

Function

生命週期函數--監聽小程序錯誤信息

當小程序調用API失敗時候會調用onError鉤子,並傳入錯誤信息。

前臺、後臺定義: 當用戶點擊左上角關閉,或者按了設備 Home 鍵離開微信,小程序並無直接銷燬,而是進入了後臺;當再次進入微信或再次打開小程序,又會從後臺進入前臺。

示意圖:

 

globalData表示小程序全局數據。

示例代碼:

App({

  onLaunch: function () {

    console.log("小程序初始化");

  },

  onShow: function () {

    console.log("小程序進入前臺");

  },

  onHide: function () {

    console.log("小程序進入後臺");

  },

  onError: function (msg) {

    console.log(msg)

  },

  globalData: '全局數據'

})

 

小程序提供了全局的 getApp()函數,能夠獲取到小程序實例。

示例代碼:

// other.js

var appInstance = getApp()

console.log(appInstance.globalData) // I am global data

注意:

  •  App() 必須在 app.js 中註冊,且不能註冊多個。
  • 不要在定義於 App() 內的函數中調用 getApp() ,使用 this 就能夠拿到 app 實例。
  • 不要在 onLaunch 的時候調用 getCurrentPage(),此時 page 尚未生成。
  • 經過 getApp() 獲取實例以後,不要私自調用生命週期函數。

 

頁面生命週期

Page()函數用來註冊一個頁面。接受一個 object 參數,其指定頁面的初始數據、生命週期函數、事件處理函數等。

object 參數說明:

 

 

  • 小程序註冊完成後,加載頁面,觸發onLoad方法。
  • 面載入後觸發onShow方法,顯示頁面。
  • 首次顯示頁面會觸發onReady方法,渲染頁面元素和樣式,一個頁面只會調用一次。
  • 當小程序後臺運行或跳轉到其餘頁面時,觸發onHide方法。
  • 當小程序有後臺進入到前臺運行或從新進入頁面時,觸發onShow方法。
  • 當使用重定向方法wx.redirectTo(OBJECT)或關閉當前頁返回上一頁wx.navigateBack(),觸發onUnload。

注意:

onLoad: 頁面加載。

  • 一個頁面只會調用一次。
  • 參數能夠獲取wx.navigateTo和wx.redirectTo及<navigator/>中的 query。

onShow: 頁面顯示

  • 每次打開頁面都會調用一次。

onReady: 頁面初次渲染完成

  • 一個頁面只會調用一次,表明頁面已經準備穩當,能夠和視圖層進行交互。
  • 對界面的設置如wx.setNavigationBarTitle請在onReady以後設置。詳見生命週期。

onHide: 頁面隱藏

  • 當navigateTo或底部tab切換時調用。

onUnload: 頁面卸載

  • 當redirectTo或navigateBack的時候調用。

 

AppService線程和View線程的交互

 

 

由上圖可知,小程序由兩大線程組成:負責界面的視圖線程(view thread)和負責數據、服務處理的服務線程(appservice thread),二者協同工做,完成小程序頁面生命週期的調用。

視圖線程有四大狀態:

  1. 初始化狀態:初始化視圖線程所須要的工做,初始化完成後向 「服務線程」發送初始化完成信號,而後進入等待狀態,等待服務線程提供初始化數據。
  2. 首次渲染狀態:當收到服務線程提供的初始化數據後(json和js中的data數據),渲染小程序界面,渲染完畢後,發送「首次渲染完成信號」給服務線程,並將頁面展現給用戶。
  3. 持續渲染狀態:此時界面線程繼續一直等待「服務線程」經過this.setdata()函數發送來的界面數據,只要收到就從新局部渲染,也所以只要更新數據併發送信號,界面就自動更新。
  4. 結束狀態:頁面被回收或者銷燬、應用被系統回收、銷燬時觸發。

 

服務線程五大狀態:

  1. 初始化狀態:此階段僅啓動服務線程所需的基本功能,好比信號發送模塊。系統的初始化工做完畢,就調用自定義的onload和onshow,而後等待視圖線程的「視圖線程初始化完成」號。onload是隻會首次渲染的時候執行一次,onshow是每次界面切換都會執行,簡單理解,這就是惟一差異。
  2. 等待激活狀態:接收到「視圖線程初始化完成」信號後,將初始化數據發送給「視圖線程」,等待視圖線程完成初次渲染。
  3. 激活狀態:收到視圖線程發送來的「首次渲染完成」信號後,就進入激活狀態既程序的正常運行狀態,並調用自定義的onReady()函數。此狀態下就能夠經過 this.setData 函數發送界面數據給界面線程進行局部渲染,更新頁面。
  4. 後臺運行狀態:若是界面進入後臺,服務線程就進入後臺運行狀態,從目前的官方解讀來講,這個狀態挺奇怪的,和激活狀態是相同的,也能夠經過setdata函數更新界面的。畢竟小程序的框架剛推出,應該後續會有很大不一樣吧。
  5. 結束狀態:頁面被回收或者銷燬、應用被系統回收、銷燬時觸發。

 

應用的生命週期對頁面生命週期的影響

 

  • 小程序初始化完成後,頁面首次加載觸發onLoad,只會觸發一次。
  • 當小程序進入到後臺,先執行頁面onHide方法再執行應用onHide方法。
  • 當小程序從後臺進入到前臺,先執行應用onShow方法再執行頁面onShow方法。

 

路由

在小程序中全部頁面的路由所有由框架進行管理。

路由方式

 

 

對於路由的觸發方式以及頁面生命週期函數以下:

 

注意:

  • navigateTo, redirectTo 只能打開非 tabBar 頁面。
  • switchTab 只能打開 tabBar 頁面。
  • reLaunch 能夠打開任意頁面。
  • 頁面底部的 tabBar 由頁面決定,即只要是定義爲 tabBar 的頁面,底部都有 tabBar。
  • 調用頁面路由帶的參數能夠在目標頁面的onLoad中獲取。

 

Tab 切換對應的生命週期

(以 A、B 頁面爲 Tabbar 頁面,C 是從 A 頁面打開的頁面,D 頁面是從 C 頁面打開的頁面爲例):

 

 

事件

定義

  • 事件是視圖層到邏輯層的通信方式。
  • 事件能夠將用戶的行爲反饋到邏輯層進行處理。
  • 事件能夠綁定在組件上,當達到觸發事件,就會執行邏輯層中對應的事件處理函數。
  • 事件對象能夠攜帶額外信息,如 id, dataset, touches。

事件示例

視圖代碼:

<!--index.wxml-->

<view>

  <view class='btn' bindtap='clickme'>點擊我

  </view>

</view>

樣式代碼:

/**index.wxss**/

.btn{

  width: 100px;

  height: 30px;

  text-align: center;

  border: 1px solid black;

  line-height: 30px;

  margin: 100px auto;

}

邏輯層處理代碼:

//index.js

Page({

  //點擊事件處理函數

  clickme: function(e) {

    console.log(e);

  }

})

控制檯輸出:

 

事件屬性說明:

Tap:觸發事件類型。

changedTouches:表示有變化的觸摸點,如從無變有(touchstart),位置變化(touchmove),從有變無(touchend、touchcancel)。

currentTarget:事件綁定的當前組件。

Target:觸發事件的源組件。

detail:自定義事件所攜帶的數據,如表單組件的提交事件會攜帶用戶的輸入,媒體的錯誤事件會攜帶錯誤信息,詳見組件定義中各個事件的定義。點擊事件的detail 帶有的 x, y 同 pageX, pageY 表明距離文檔左上角的距離。

 

小程序事件模型

和JavaScript的事件模型相似

 

 

  • 事件捕獲階段:所綁定的事件,會從最外層節點傳播到目標節點。依次檢查所通過的節點是否綁定了相同類型的監聽回調函數;若是有,則執行相應回調函數。
  • 事件處理階段:事件到達目標節點,執行綁定的監聽回調函數。
  • 事件冒泡階段:事件從目標節點向外冒泡,依次檢查所通過節點是否綁定了相同類型的監聽回調函數;若是有則執行回調函數。

 

可捕獲和可冒泡事件

 

tap表示按壓屏幕小於350ms。

longpress:按壓時間超過350ms的事件操做,不會再觸發tap操做。(長按操做推薦)

longtap:觸發以後會繼續觸發tap操做。

 

事件模型示例代碼

樣式文件:

/* pages/index/index.wxss */

#outter{

  border: 1px red solid;

  background-color: red;

  width: 80%;

  height: 200px;

  margin: 100px auto;

  text-align: center;

}

 

#middle{

  border: 1px white solid;

  background-color: white;

  width: 80%;

  height: 120px;

  margin: 20px auto;

}

 

#inner{

  border: 1px yellow solid;

  background-color: yellow;

  width: 80%;

  height: 50px;

  margin: 10px auto;

}

 

事件回調函數定義:

// pages/index/index.js

Page({

  click1() {

    console.log("冒泡:點擊了C1");

  },

  click2() {

    console.log("冒泡:點擊了C2");

  },

  click3() {

    console.log("冒泡:點擊了C3");

  },

  click11() {

    console.log("捕獲:點擊了C1");

  },

  click22() {

    console.log("捕獲:點擊了C2");

  },

  click33() {

    console.log("捕獲:點擊了C3");

  }

})

視圖層代碼:

<!--pages/index/index.wxml-->

<view id="outter" bindtap='click1' capture-bind:tap="click11">

  外層容器C1

  <view id="middle"  bindtap='click2' capture-bind:tap="click22">

    中間層容器C2

    <view id="inner"  bindtap='click3' capture-bind:tap="click33">

      內層容器C3

    </view>

  </view>

</view>

點擊C1,測試結果:

 

 分別點擊C三、C二、C1,測試結果:

 

 

注意:

  1. bindtap不會阻止事件的冒泡。
  2. capture-catch能夠阻止冒泡。示例代碼以下:

<!--pages/index/index.wxml-->

<view id="outter" bindtap='click1' capture-bind:tap="click11">

  外層容器C1

  <view id="middle"  bindtap='click2' capture-bind:tap="click22">

    中間層容器C2

    <view id="inner"  bindtap='click3' capture-catch:tap="click33">

      內層容器C3

    </view>

  </view>

</view>

測試效果,點擊C3容器:

 

相關文章
相關標籤/搜索