前言這篇文章主要是對(duì)小程序官方文檔做部分深入解讀;讓大家了解小程序?qū)崿F(xiàn)背后的機(jī)制原理。 由于筆者沒(méi)有獲取到微信官方提供的小程序?qū)崿F(xiàn)原理圖,很多內(nèi)容都是通過(guò)閱讀文檔資料反推和理解所得,如有誤解之處,望指正。 本文建議閱讀時(shí)間: 5min 目錄
小程序SDK官方文檔稱其為“基礎(chǔ)庫(kù)”。 這是一個(gè)很寬泛的名詞,只是覺(jué)得很重要,但說(shuō)不好它具體有什么作用。我總結(jié)了 小程序5月帶給我的驚喜 ,它支持的功能也越來(lái)越豐富,體驗(yàn)也越變?cè)胶茫@些動(dòng)作如果用專業(yè)一點(diǎn)的術(shù)語(yǔ)概括就是“升級(jí)基礎(chǔ)庫(kù)”。 微信從開(kāi)放內(nèi)測(cè)到現(xiàn)在,微信基礎(chǔ)庫(kù)已經(jīng)從的 v1.0.0 升級(jí)到了 v1.3.0 。 那么,“基礎(chǔ)庫(kù)”到底是什么東西? 小程序基礎(chǔ)庫(kù)提供豐富的微信原生API,可以方便的調(diào)起微信提供的能力,如獲取用戶信息,本地存儲(chǔ),支付功能等。 這是官方對(duì)于“基礎(chǔ)庫(kù)”的定義。我們知道小程序的開(kāi)發(fā)十分類似于現(xiàn)在的移動(dòng)web開(kāi)發(fā),而移動(dòng)web能使用到手機(jī)系統(tǒng)功能,app特色功能是非常有限的,而“基礎(chǔ)庫(kù)”的作用就是為了拓展小程序這方面能力,讓其功能與表現(xiàn)更接近原生app。 JS-SDK我們發(fā)現(xiàn)“基礎(chǔ)庫(kù)”的功能和微信的 JS-SDK 十分類似,順便再回顧下微信 JS-SDK 又是做什么的呢? 微信 JS-SDK 是微信公眾平臺(tái)面向網(wǎng)頁(yè)開(kāi)發(fā)者提供的基于微信內(nèi)的網(wǎng)頁(yè)開(kāi)發(fā)工具包。通過(guò)使用微信 JS-SDK ,網(wǎng)頁(yè)開(kāi)發(fā)者可借助微信高效地使用拍照、選取圖片、語(yǔ)音、位置等手機(jī)系統(tǒng)的能力,同時(shí)可以直接使用微信分享、掃一掃、卡券、支付等微信特色功能,為微信用戶提供更優(yōu)質(zhì)的網(wǎng)頁(yè)體驗(yàn)。 小程序基礎(chǔ)庫(kù)與JS-SDK的共同點(diǎn)
小程序基礎(chǔ)庫(kù)與JS-SDK的不同點(diǎn)
小程序基礎(chǔ)庫(kù)查看方法官方API : getSystemInfo 、 getSystemInfoSync 。
wx.getSystemInfo({
success: function(obj) {
obj = obj || {};
console.log('SDKVersion: ', obj.SDKVersion);
},
fail: function() {
console.error('[error]: getSystemInfo failed.');
}
});
返回對(duì)象中 SDKVersion 的值就是該微信版本攜帶的小程序基礎(chǔ)庫(kù)的版本號(hào)。 注意:該屬性在小程序版本 v1.1.0 才開(kāi)始支持。 小程序基礎(chǔ)庫(kù)與客戶端之間的關(guān)系小程序的能力需要微信客戶端來(lái)支撐,每一個(gè)基礎(chǔ)庫(kù)都只能在對(duì)應(yīng)的客戶端版本上運(yùn)行,高版本的基礎(chǔ)庫(kù)無(wú)法兼容低版本的微信客戶端。 官方的這種說(shuō)法存在一些問(wèn)題?,F(xiàn)在基礎(chǔ)庫(kù)版本和客戶端版本并不是一一對(duì)應(yīng)關(guān)系。客戶端可以主動(dòng)升級(jí)小程序基礎(chǔ)庫(kù)版本達(dá)到灰度上線新版的目的,所以必然存在一個(gè)客戶端版本對(duì)應(yīng)多個(gè)基礎(chǔ)庫(kù)版本的情況。 小程序基礎(chǔ)庫(kù)更新時(shí)機(jī)為了避免新版本的基礎(chǔ)庫(kù)給線上小程序帶來(lái)未知的影響,微信客戶端都是攜帶 上一個(gè)穩(wěn)定版 的基礎(chǔ)庫(kù)發(fā)布的。 在新版本客戶端發(fā)布后,我們?cè)偻ㄟ^(guò)后臺(tái)灰度新版本基礎(chǔ)庫(kù),灰度時(shí)長(zhǎng)一般為 12 小時(shí),在灰度結(jié)束后,用戶設(shè)備上才會(huì)有新版本的基礎(chǔ)庫(kù)。 以微信 6.5.8 為例,客戶端在發(fā)布時(shí)攜帶的是 1.1.1 基礎(chǔ)庫(kù)(6.5.7上已全量的穩(wěn)定版)發(fā)布,在 6.5.8 發(fā)布后,我們?cè)偻ㄟ^(guò)后臺(tái)灰度 1.2.0 基礎(chǔ)庫(kù)。 筆者使用的IOS設(shè)備更新到v6.5.8時(shí)對(duì)應(yīng)的 SDKVersion 是 1.2.0, 但截止發(fā)稿日,該值已經(jīng)變成了 1.2.4。 “細(xì)思恐極”,如果我們已經(jīng)完成一臺(tái)裝有 6.5.4 版本微信的Oppo手機(jī)對(duì)小程序的兼容測(cè)試,很有可能過(guò)幾天這臺(tái)Oppo手機(jī)將小程序基礎(chǔ)庫(kù)更新到新版本導(dǎo)致小程序不可用。 建議:了解產(chǎn)品的用戶手機(jī)微信版本分布,確定回歸覆蓋范圍,完成回歸測(cè)試。 微信版本分布![]() ![]() 這是我們點(diǎn)餐小程序中用戶各版本微信的占比圖。通過(guò)這兩張圖我們能夠清晰的得出目前用戶使用的微信版本分布,就能夠制定針對(duì)性的測(cè)試覆蓋方案。 微信版本和基礎(chǔ)庫(kù)版本在 基礎(chǔ)庫(kù)更新時(shí)機(jī) 那小結(jié)我們提到,小程序的基礎(chǔ)庫(kù)和微信版本并不是一一對(duì)應(yīng)關(guān)系,且隨著基礎(chǔ)庫(kù)和微信的不斷升級(jí),它們的對(duì)應(yīng)關(guān)系在時(shí)刻發(fā)生著變化。 下面是我整理的小程序基礎(chǔ)庫(kù)和微信版本對(duì)應(yīng)表。 ![]() 開(kāi)發(fā)者工具切換基礎(chǔ)庫(kù)版本![]() 在使用新API、新特性的時(shí)候,可以通過(guò)切換基礎(chǔ)庫(kù)版本完成兼容性測(cè)試。 wx 對(duì)象小程序基礎(chǔ)庫(kù)的所有功能全部封裝在 wx 全局對(duì)象中,開(kāi)發(fā)者可以在小程序邏輯層代碼的任何地方調(diào)用該對(duì)象的方法。 看到 wx 對(duì)象,使用過(guò) JS-SDK 的朋友表示很熟悉。我們?cè)谇懊嬲鹿?jié) 小程序基礎(chǔ)庫(kù)與JS-SDK對(duì)它們做了簡(jiǎn)單的比較,下面深入學(xué)習(xí)它們出于同源的技術(shù): JsBridge 。 下一節(jié),我們?cè)敿?xì)講解一下 JsBridge 的原理和實(shí)現(xiàn)。 JsBridgeNative層需要暴露一些方法給js調(diào)用,比如,彈Toast提醒,彈Dialog,分享等等,有時(shí)候甚至把h5的網(wǎng)絡(luò)請(qǐng)求放著native去完成。原因很簡(jiǎn)單,Native有更好的性能,更一致的表現(xiàn)和體驗(yàn)。這就是 JsBridge 技術(shù)。 webview與客戶端的關(guān)系webview是客戶端的一個(gè)可調(diào)用的組件,且客戶端可以對(duì)webview做函數(shù),事件的封裝,可以攔截webview的請(qǐng)求做統(tǒng)一控制,也可以向webview注入方法供JS調(diào)用等。 兩種 JsBridge 實(shí)現(xiàn)思路
通過(guò)這些方式能夠?qū)崿F(xiàn)JS和客戶端通信,調(diào)起客戶端以及手機(jī)系統(tǒng)能力。 微信小程序 JsBridge 的實(shí)現(xiàn)wx 就是客戶端暴露給小程序的 JsBridge 接口。而這個(gè)封裝的 JsBridge 非常強(qiáng)大,它不僅僅支持toast,彈框等簡(jiǎn)單功能,甚至包括網(wǎng)路 request 請(qǐng)求,緩存操作,手機(jī)硬件設(shè)備藍(lán)牙、重力感應(yīng)等。下一個(gè)章節(jié),我們介紹微信如何使用 JsBridge 、視圖層、邏輯層交互操作。 通信機(jī)制架構(gòu)介紹![]() 這張圖展示了視圖層、邏輯層之間通信方式,以及 JsBridge 起到紐帶的作用。我們可以做以下幾點(diǎn)總結(jié):
執(zhí)行環(huán)境三端的js執(zhí)行環(huán)境以及用于渲染非原生組件的環(huán)境是各不相同的:
這是官方關(guān)于視圖層和邏輯層運(yùn)行環(huán)境的介紹。 視圖層(不包括Native組件)在webview組件中渲染;邏輯層的js代碼則通過(guò)js容器來(lái)執(zhí)行。js代碼執(zhí)行過(guò)程中可以通過(guò)JsBridge調(diào)起微信、系統(tǒng)的能力,也可以將數(shù)據(jù)通過(guò) setData() 函數(shù)傳遞給視圖層做渲染。視圖層在渲染和交互過(guò)程中可以通過(guò)事件與客戶端進(jìn)行通信。 視圖層向邏輯層的通信由事件來(lái)完成,而邏輯層數(shù)據(jù)向視圖層發(fā)送渲染指令是通過(guò) Page 的 setData 函數(shù)。下面我們?cè)敿?xì)說(shuō)明他們的細(xì)節(jié)原理; 事件![]() 這張生命周期圖非常詳盡的描述了一個(gè)頁(yè)面從創(chuàng)建入棧、數(shù)據(jù)交互、銷毀出棧的整個(gè)過(guò)程。在頁(yè)面渲染和使用過(guò)程中會(huì)出現(xiàn)大量的事件,而這些事件會(huì)被JsBridge捕獲到,并傳遞給邏輯層處理,主要包括:生命周期事件、UI事件。 生命周期事件視圖進(jìn)程在完成階段性工作后,需要向邏輯層同步其當(dāng)前狀態(tài)以便邏輯層做出應(yīng)對(duì)策略。主要包括: onLoad 、 onReady 、 onShow 、 onHide 、 onUnload 、 onPullDownRefresh 、 onReachBottom 、 onShareAppMessage 等。 UI事件視圖層向邏輯層的通信方式。 這類事件綁定在組件上,觸發(fā)則可以將用戶的行為反饋到邏輯層對(duì)應(yīng)的注冊(cè)函數(shù),如 bindtap、 bindinput 、 bindconfirm 、 bindfocus 、 bindsubmit 、 bindchange 、 bindlinechange 等 Page.prototype.setData()邏輯層向視圖層發(fā)送渲染指令的方式。 setData 函數(shù)用于將數(shù)據(jù)從邏輯層發(fā)送到視圖層,同時(shí)改變對(duì)應(yīng)的 this.data 的值。 注意:data 將會(huì)以 JSON 的形式由邏輯層傳至渲染層,所以其數(shù)據(jù)必須是可以轉(zhuǎn)成 JSON 的格式:字符串,數(shù)字,布爾值,對(duì)象,數(shù)組。 從性能角度考慮,UI渲染是比較耗時(shí)的,一方面盡量減少頁(yè)面渲染操作,另一方面其會(huì)對(duì) data的內(nèi)容做深層次diff,盡量控制 data 的大小和數(shù)據(jù)結(jié)構(gòu)的深度(官方限制 data 數(shù)據(jù)量最大為1024K。 筆者在做小程序性能監(jiān)控的時(shí)候,遇到的一個(gè)問(wèn)題就是無(wú)法準(zhǔn)確統(tǒng)計(jì)到頁(yè)面渲染完成的時(shí)間。原因是 setData 函數(shù)只負(fù)責(zé)發(fā)送數(shù)據(jù)和渲染指令,但并沒(méi)有對(duì)應(yīng)的反饋通知。 通過(guò)仔細(xì)分析,微信其實(shí)是可以提供這樣的能力的,在視圖層完成渲染后通過(guò)某種方式再告知邏輯層渲染狀態(tài),我想出了兩個(gè)解決方案:
在和騰訊的同事提出該建議后,他們給出否定答案。原因是:回調(diào)函數(shù)會(huì)增加一次渲染層到邏輯層的通信開(kāi)銷,而這個(gè)開(kāi)銷相對(duì)而已比較重,目前他們官方是做了一些數(shù)據(jù)采樣,下圖是他們發(fā)給我們的渲染結(jié)果。 ![]() 總結(jié)本文通過(guò)對(duì)小程序基礎(chǔ)庫(kù)的介紹、拓展 JsBridge 到融會(huì)貫通,介紹基礎(chǔ)庫(kù)、視圖層、邏輯層的運(yùn)行機(jī)制,就是希望大家能夠建立對(duì)小程序架構(gòu)理解,了解各模塊在框架中所處位置和功用。 以上均為個(gè)人理解所得,由于沒(méi)有一些系統(tǒng)學(xué)習(xí)客戶端開(kāi)發(fā)webview的經(jīng)驗(yàn),所以關(guān)于 JsBridge 的一些講解比較淺顯,如有不當(dāng)言論,望指出,謝謝。 -作者介紹- J文,美團(tuán)點(diǎn)評(píng)點(diǎn)餐終端團(tuán)隊(duì)C端組成員,負(fù)責(zé)大眾點(diǎn)評(píng)云餐廳微信小程序開(kāi)發(fā)。 |