【小程式探索】:深入理解小程式中的數據
- 2019 年 12 月 16 日
- 筆記
剛開始擼小程式的時候,覺得看看文檔就可以了,導致寫了很多垃圾程式碼坑人坑己,相信大部分初學者也不會去仔細研究文檔,更別說啰里啰嗦的指南了,在通讀小程式官方指南後,很有必要總結一番。清楚了生命周期和數據通訊,就能對整個程式有一定的把控能力,定位問題和解決問題的能力將大幅提高。
天生的延時
- 為了解決管控與安全問題,小程式提供了一個沙箱環境來運行開發者的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 的方案:
