研究背景

基礎篇中瞭解了小程序的生命週期,接下來研究一下數據通信,我覺得清楚了生命週期和數據通信,就能對整個程序有一定的把控能力,定位問題和解決問題的能力將大幅提高

我剛開始擼小程序的時候,覺得看看文檔就可以了,導致寫了很多垃圾代碼坑人坑己,相信大部分初學者也不會去仔細研究文檔,更別說囉裏囉嗦的指南了,在通讀小程序官方指南後,我覺得很有必要為初學者總結一番,教學相長 (作者:周振超

天生的延時

  • 為瞭解決管控與安全問題,小程序提供了一個沙箱環境來運行開發者的JavaScript 代碼
  • 基於雙線程模型,意味著任何數據傳遞都是線程間的通信
  • 在小程序架構裏,這一切都會變成非同步
  • 非同步會使得各部分的運行時序變得複雜一些,因此邏輯層與渲染層需要有一定的機制保證時序正確
  • 這些工作在小程序框架裏會處理好,開發者只需要理解生命週期,以及控制合適的時機更新UI即可
  • 上一篇文章我們學習了小程序的生命週期,本文主要理解如何控制合適的時機更新UI

如何控制合適的時機更新UI

小程序作為MVVM框架中的一員,數據驅動是核心,得數據者得天下

  • 要理解數據通信,和生命週期、運行機制密不可分,像雙線程通信模型、數據驅動、底層框架、界面渲染機制等等,本文不會展開敘述,也不可能講的比官方文檔更好、更實時
  • 本文主要理解以下幾點:(想了半天,才概括如下)
  • 1、小程序中數據的作用域
  • 2、合理操作數據,提升性能
  • 3、組件間的數據通信
  • 4、緩存數據
  • 5、擴展-狀態管理westore

在這之前,還是上幾張官方的圖,有個概念便於後續理解

明確幾點概念

  • 渲染層和數據相關
  • 邏輯層負責產生、處理數據,小程序的JS腳本運行在同一個JsCore線程裏
  • 邏輯層和渲染層是一對多的關係,但頁面對象(page)和頁面層級(webview)一一對應

一、小程序中數據的作用域

1、全局數據

// app.js
App({
globalData: I am global data // 全局共享數據
})
// 其他頁面腳本other.js
var appInstance = getApp()
console.log(appInstance.globalData) // 輸出: I am global data

  • App實例是單例的,因此不同頁面直接可以通過App實例下的屬性來共享數據

2、頁面共享數據

  • 簡單來說就是頁面所在的JS中Page構造器外定義的變數
  • 執行如下示例代碼以驗證

console.log(載入 page.js)
var count = 0
Page({
onLoad: function() {
count += 1
console.log(第 + count + 次啟動這個頁面)
}
})

  • 你會發現小程序啟動時,列印了載入 page.js,每次打開這個頁面,count變數會遞增,不會隨著頁面的銷毀而銷毀
  • 由於頁面所在的JS文件、app.js和所有其他被require的JS文件,在小程序啟動時自動執行並被基礎庫註冊,所以邏輯層(看作所有js的集合)只執行一次,之後都是通過Page構造器創建Page實例來渲染頁面
  • 一般require的依賴或者第三方庫JS以及getApp(),我們都會放在頁面共享的數據中

3、Page實例中的數據

  • 也就是每個Page構造器中的數據,沒錯!這就是我們每天搬磚的地方

Page({
data: { text: "我用來改變界面顯示" },
onLoad: function(options) { },
onReady: function() { },
onShow: function() { },
onHide: function() { },
onUnload: function() { },
text: "我不顯示在頁面上",
myData:{
a: 我也不顯示在頁面上,
b: true
}
})

  • 大家應該都知道data中的數據用來渲染頁面,和VUE一樣,不過VUE中只要寫this.text,而小程序中要寫this.data.text,每次寫到這個就鬱悶,其實與界面渲染無關的數據最好不要設置在data中,對性能也是大有好處

4、自定義組件中的數據

  • properties外部傳值
  • data內部數據
  • emmmmmm自定義組件有必要另開一篇總結

二、合理操作數據,提升性能

數據通信

  • 頁面初始數據通信:視圖層在接收到初始數據data時,進行初始渲染
  • 更新數據通信:視圖層在接收到更新數據setData時,進行重渲染
  • 用戶事件通信:一個用戶事件被觸發,視圖層會將信息反饋給邏輯層
  • 一切都是2個線程通信的結果,數據量小於64KB時總時長可以控制在30ms內。傳輸時間與數據量大體上呈現正相關關係,傳輸過大的數據將使這一時間顯著增加。因而減少傳輸數據量是降低數據傳輸時間的有效方式

提升性能須遵循的原則

調用setData執行重渲染時,視圖層將data和setData數據套用在WXML片段上,得到一個新節點樹,然後與當前節點樹進行比較,這樣可以得到哪些節點的哪些屬性需要更新、哪些節點需要添加或移除,最後,將setData數據合併到data中,並用新節點樹替換舊節點樹,用於下一次重渲染。

可以看出邏輯層setData發送數據給更新視圖時,需要兩個線程的一些通信消耗,且不會diff數據,只會一股腦傳過去,生成新節點樹,每一次通信都需要經過傳輸、生成、比較、合併

為了提升數據更新的性能,最好遵循以下原則:

  • 1、不要過於頻繁調用setData,應考慮將多次setData合併成一次setData調用
  • 2、數據通信的性能與數據量正相關,每次只設置需要改變的最小單位數據
  • 3、與界面渲染無關的數據最好不要設置在data中,可以考慮設置在page對象的其他欄位下

其他優化策略:

  • 1、去掉不必要的事件綁定(WXML中的bind和catch),從而減少通信的數據量和次數
  • 2、事件綁定時需要傳輸target和currentTarget的dataset,因而不要在節點的data前綴屬性中放置過大的數據
  • 3、精簡代碼,降低WXML結構和JS代碼的複雜性,必要時使用分包優化

注意:

  • 直接修改 Page實例的this.data 而不調用 this.setData 是無法改變頁面的狀態的,還會造成數據不一致
  • 不要把data中的任意一項的value設為undefined,否則可能會有引起一些不可預料的bug

三、組件間的數據通信

組件區分業務組件和純組件

  • 業務組件與業務數據緊耦合,換一個項目可能該組件就用不上,除非非常類似的項目
  • 業務組件和頁面一樣通過 全局變數 獲得所需參數,通過更改 全局變數 與外界通訊
  • 業務組件也可以通過 props 獲得所需參數,通過 triggerEvent 與外界通訊
  • 純組件與業務數據無關,可移植和復用
  • 純組件只能通過 props 獲得所需參數,通過 triggerEvent 與外界通訊

四、緩存數據

本地數據緩存是小程序存儲在當前設備上硬碟上的數據,小程序宿主環境從不同小程序和不同用戶兩個維度來隔離緩存空間,每個小程序的緩存空間上限為10MB

緩存充當全局數據

  • 通過wx.getStorage/wx.getStorageSync讀取本地緩存
  • 通過wx.setStorage/wx.setStorageSync寫數據到緩存

利用本地緩存提前渲染界面

  • 我們在拉取商品列表後把列表存在本地緩存裏
  • 在onLoad發起請求前,先檢查是否有緩存過列表
  • 如果有的話直接渲染界面
  • 等到wx.request的success回調之後再覆蓋本地緩存重新渲染新的列表

Page({
onLoad: function() {
var that = this
var list =wx.getStorageSync("list")
if (list) { // 本地如果有緩存列表,提前渲染
that.setData({
list: list
})
}
wx.request({
url: https://test.com/getproductlist,
success: function (res) {
if (res.statusCode === 200) {
list = res.data.list
that.setData({ // 再次渲染列表
list: list
})
wx.setStorageSync("list",list) // 覆蓋緩存數據
}
}
})
}
})

  • 一般在對數據實時性/一致性要求不高的頁面採用這個方法來做提前渲染,用以優化小程序體驗

五、擴展-狀態管理westore

引用

眾所周知,小程序通過頁面或組件各自的 setData 再加上各種父子、祖孫、姐弟、姑姑與堂兄等等組件間的通訊會把程序搞成一團漿糊,如果再加上跨頁面之間的組件通訊,會讓程序非常難維護和調試。雖然市面上出現了許多技術棧編譯轉小程序的技術,但是我覺沒有戳中小程序的痛點。小程序不管從組件化、開發、調試、發布、灰度、回滾、上報、統計、監控和最近的雲能力都非常完善,小程序的工程化簡直就是前端的典範。而開發者工具也在持續更新,可以想像的未來,組件佈局的話未必需要寫代碼了。而且據統計,開發小程序使用最多的技術棧是使用小程序本身的開發工具和語法,所以最大的痛點只剩下狀態管理和跨頁通訊
  • 現在主流的MVVM框架如vue/react/angluar都有狀態管理,小程序也可以有,由於小程序的即時特性,迭代更新非常快,所以對於小程序我是崇尚原生開發的,不過多端合一也是很nice的解決方案,自己玩的時候當然要試試dcloud公司的uniapp
  • 廢話不多說,直接貼圖和鏈接,有興趣的自行研究哈,Westore 的方案:

Tencent/westore?

github.com
圖標

推薦閱讀:
相關文章