微信小程序基礎架構(gòu)淺析
微信小程序,簡稱小程序,英文 mini program。是一種不需要下載安裝即可在微信中使用的應用,用戶掃描小程序碼或搜索小程序即可打開,觸手可及,用完即走,不用關(guān)心是否安裝太多應用的問題。
小程序技術(shù)演進
內(nèi)部開放微信原生能力
使用 WeixinJSBridge 預覽圖片
此類 API 最初是提供給騰訊內(nèi)部一些業(yè)務使用,很多外部開發(fā)者發(fā)現(xiàn)了之后,依葫蘆畫瓢地使用了,逐漸成為微信中網(wǎng)頁開發(fā)的事實標準。
JS-SDK 發(fā)布
2015 年初,微信發(fā)布了一整套網(wǎng)頁開發(fā)工具包,稱之為 JS-SDK,開放了拍攝、錄音、語音識別、二維碼、地圖、支付、分享、卡券等幾十個 API。讓所有開發(fā)者都可以使用到微信的原生能力。
使用 JS-SDK 調(diào)用圖片預覽組件
JS-SDK 解決了移動網(wǎng)頁使用微信能力不足的問題,通過暴露微信的接口使得 Web 開發(fā)者能夠擁有更多的能力,然而在更多的能力之外,JS-SDK 的模式并沒有解決使用移動網(wǎng)頁遇到的體驗不良的問題。
JS-SDK 的不足
用戶在訪問網(wǎng)頁的時候,在瀏覽器開始顯示之前都會有一個白屏的過程,在移動端,受限于設備性能和網(wǎng)絡速度,白屏會更加明顯。除了白屏,影響 Web 體驗的問題還有缺少操作的反饋,主要表現(xiàn)在兩個方面:頁面切換的生硬和點擊的遲滯感。
加載白屏,切換不流暢
此外一些開發(fā)者會使用 JS-SDK 做一些,比如假紅包,偽造的官方活動等。并利用 JS-SDK 的分享能力變相的去裂變分享到各個群或者朋友圈,由于 JS-SDK 是根據(jù)域名來賦予 api 權(quán)限的,運營人員封了一個域名后,他們立馬用別的域名又繼續(xù)做壞,要知道注冊一個新的域名的成本是很低的。
那么小程序是通過怎樣的設計來改進 JS-SDK 的體驗和管控上的不足?
小程序雙線程架構(gòu)
具體實現(xiàn)上小程序采用了類 web + 離線包的形式。開發(fā)上與 web 類似,門檻較低,開發(fā)效率較高。離線下載和頁面預渲染功能增強了用戶體驗,提升了加載速度,解決了 JS-SDK 加載白屏的問題 1。小程序提供了云端更新離線包的功能,可動態(tài)更新頁面,相對于 app 的更新和發(fā)布更為靈活。此外,小程序在離線包的基礎上對切換動畫進行優(yōu)化,降低了切換頁面導致的遲滯感,緩解了切換不流暢的問題 2。
小程序web+離線包模式
小程序在架構(gòu)方面最大的特點是采用了雙線程的開發(fā)模式,隔離了 JS 邏輯和 UI 渲染。小程序的渲染層和邏輯層分別由 2 個線程管理:渲染層的界面使用了 WebView 進行渲染,邏輯層采用 JsCore 線程運行 JS 腳本。
邏輯層:創(chuàng)建一個單獨的線程去執(zhí)行 JavaScript,在這個環(huán)境下執(zhí)行的都是有關(guān)小程序業(yè)務邏輯的代碼;
渲染層:界面渲染相關(guān)的任務全都在 WebView 線程里執(zhí)行,通過邏輯層代碼去控制渲染哪些界面。一個小程序存在多個界面,所以渲染層存在多個 WebView 線程;
通信:這兩個線程的通信會經(jīng)由微信客戶端(下文中也會采用 Native 來代指微信客戶端)做中轉(zhuǎn),邏輯層發(fā)送網(wǎng)絡請求也經(jīng)由 Native 轉(zhuǎn)發(fā),小程序的通信模型下圖所示。
小程序雙線程架構(gòu)
JS 邏輯層運行在 JSCore 中,并沒有一個完整瀏覽器對象,因而缺少相關(guān)的 DOM API 和 BOM API,無法操作頁面元素,能達到管控的目的,但也限制了開發(fā)者的權(quán)限:
- 不允許開發(fā)者把頁面跳轉(zhuǎn)到其他在線網(wǎng)頁
- 不允許開發(fā)者直接訪問 DOM
- 不允許開發(fā)者隨意使用 window 上的某些未知的可能有危險的 API
這樣的邏輯層與 UI 層的隔離,加上小程序的審核和舉報機制,使得微信加強對小程序的管控,解決了問題 3。但這也使得開發(fā)者無法靈活的進行頁面渲染。
小程序頁面渲染
上面已經(jīng)說了邏輯層無法操作 DOM 變更,那小程序是如何進行頁面的渲染呢?小程序基于數(shù)據(jù)驅(qū)動的架構(gòu)模式,基于 Virtual Dom(React 引入,真實 DOM 的一種 JS 描述方式)的概念,業(yè)務側(cè)只需要改變數(shù)據(jù)即可引起界面變化。其中渲染層提供了帶有數(shù)據(jù)綁定語法的 WXML,邏輯層提供了setData 等等 API,開發(fā)者需要進行界面變化時,只需要通過在邏輯層執(zhí)行 setData 把變化的數(shù)據(jù)通過 Native 層傳遞到渲染層,小程序會進行 Dom Diff(DOM 結(jié)構(gòu)對比并進行最小化變更的算法)等流程,最后把正確的結(jié)果更新在 Dom 樹上。
小程序Virtual DOM渲染
完整的通信流程大致如下:
- 邏輯層調(diào)用宿主環(huán)境的 setData 方法。
- 邏輯層將待傳輸數(shù)據(jù)轉(zhuǎn)換成字符串,并拼接到特定的 JS 腳本,最后將數(shù)據(jù)傳輸?shù)戒秩緦印?/li>
- 渲染層接收到后,WebView JS 線程會對腳本進行編譯,得到待更新數(shù)據(jù)后進入渲染隊列,等待 WebView 線程空閑時進行頁面渲染。
- WebView 線程開始執(zhí)行渲染時,待更新數(shù)據(jù)會合并到視圖層保留的原始 data 數(shù)據(jù),并將新數(shù)據(jù)套用在 WXML 片段中得到新的虛擬節(jié)點樹。經(jīng)過新虛擬節(jié)點樹與當前節(jié)點樹的 diff 對比,將差異部分更新到 UI 視圖。同時將新的節(jié)點樹替換舊節(jié)點樹,用于下一次重渲染。
小程序方案與 React Native 對比
那么小程序與現(xiàn)有的混合開發(fā)技術(shù)類型的異同點在哪?尤其是與 React Native 的區(qū)別,小程序技術(shù)架構(gòu)為什么沒有使用 React Native?
混合開發(fā)技術(shù)類型
現(xiàn)有的混合開發(fā)類型,基于 UI 渲染的分類來看,主要有兩類:
- 基于 WebView UI 的基礎方案。市面上主流,例如微信 JS-SDK,通過 JSBridge 完成 H5 和 Native 的雙向通訊,從而賦予 H5 一定的原生能力。
- 基于 Native UI 的方案,例如 React-Native、Weex、Flutter 等。在賦予 H5 原生 API 能力的基礎上,進一步通過 JSBridge 將 JS 解析成虛擬 DOM 傳遞到 Native,并使用原生渲染。
小程序也屬于類型 1,本次我們主要以類型 2 中的 React Native 作為對比分析。
React Native 技術(shù)架構(gòu)
框架
React Native 框架主要有三層:
- JS 層:該層提供了各種供開發(fā)者使用的組件以及一些工具庫(事件分發(fā)等)。
- C++層:主要處理 java/OC 與 JS 的通信(JSBridge)以及執(zhí)行 JavaScript(JS 腳本引擎)。
- Native 層(Object C/Java 層):主要包括 UI 渲染器、網(wǎng)絡通信等工具庫。根據(jù)不同操作系統(tǒng)有不同的實現(xiàn)。
UI 渲染
React Native 基于 react 框架(Virtual Dom)來進行 UI 渲染,具體的流程大致如下:
- 首先 JS 層通過 JSX 編寫的 Virtual Dom 來構(gòu)建 Component
- Native 層將其轉(zhuǎn)成真實 DOM 插入到原生 App 的頁面中。
- 當有變更,通過 diff 算法生成差異對象
- 最終由 Native 層將差異對象應用到原生 App 的頁面元素上。
通信
React Native 基于 JSCore 實現(xiàn) js 與 java/oc 交互,具體流程大致如下:
- 把 JSX 代碼解析成 javaScript 代碼
- 讀取 JS 文件,并利用利用 JS 腳本引擎執(zhí)行
- 返回一個數(shù)組,數(shù)組中會描述 OC/Java 對象,描述對象屬性和所需要執(zhí)行的方法,這樣就能讓這個對象設置屬性,并且調(diào)用方法。
Reactive Native架構(gòu)
React Native 優(yōu)缺點
優(yōu)勢
- 原生渲染,性能更好,用戶體驗較好;
- React 生態(tài)較好,對前端開發(fā)友好;
- hybrid 技術(shù)跨平臺開發(fā),成本及難度低于原生;
- 可熱更新,能夠方便迭代。
劣勢
- 支持的樣式是 CSS 的子集,會滿足不了 Web 開發(fā)者日漸增長的需求;
- 現(xiàn)有能力下還存在的一些不穩(wěn)定問題,比如性能、Bug 等;
- 把渲染工作全都交由客戶端原生渲染,會有更接近原生的體驗,但實際上一些簡單的界面元素使用 Web 技術(shù)渲染完全能勝任;
- React Native 之前爆出了一個開源協(xié)議問題(Facebook BSD+Patents ,大致內(nèi)容是使用基于 Facebook BSD+Patents 協(xié)議的開源項目的開發(fā)者,未來要是因為專利問題與 Facebook 產(chǎn)生糾紛,那么 Facebook 將有權(quán)停止你使用該開源項目),這對于之后也是存在隱患的。
小程序不選擇 React Native 原因
據(jù)小程序開發(fā)人員告知的原因如下:
- React Native 只支持 CSS 的子集,作為一個開放的生態(tài),需要告知開發(fā)者哪些 CSS 屬性能用,哪些不能用,這樣的開發(fā)體驗較差;(對應上面的劣勢 1)
- React Native 本身存在一些問題,這些依賴 RN 的修復,同時這樣就變成太過依賴客戶端發(fā)版本去解決開發(fā)者那邊的 Bug,修復周期太長。(對應上面的劣勢 2)
- React Native 前陣子還搞出了一個開源協(xié)議問題,來說也是存在隱患的。(對應上面的劣勢 4)
小程序與 React Native 相同點
- 都具有 hybrid 技術(shù)的優(yōu)點:接近原生的體驗,跨平臺開發(fā)
- 使用 Web 相關(guān)技術(shù)框架來編寫業(yè)務代碼,React Native 為 React 框架,小程序為小程序開發(fā)框架。
- 各自實現(xiàn)了跨語言通訊方案完成 Native(Java/Objective-c/…)端與 JavaScript(小程序中為渲染層和邏輯層)的通訊
小程序與 React Native 不同點
小程序使用瀏覽器內(nèi)核 WebView 來渲染界面(小部分原生組件由客戶端參與渲染),界面主要由成熟的 Web 技術(shù)渲染,輔之大量的接口提供豐富的客戶端原生能力,而 React Native 是客戶端原生渲染。理論上 React Native 相對于 WebView 的性能更好,但小程序的類 web 開發(fā)對開發(fā)來說入門相對簡單,像是一種開發(fā)效率與性能的雙刃劍。
小程序開發(fā)注意事項
基于上面的架構(gòu)分析,我們在開發(fā)中需要注意是:
- 避免使用操作操作 DOM 的 npm 包。由于邏輯層和渲染層隔離,邏輯層無法操作 DOM/BOM API,所以需要使用 DOM/BOM API 相關(guān)的 npm 包和庫中不可使用。
- 避免頻繁調(diào)用setData。由于setData中的數(shù)據(jù)不僅需要通過 Native 層傳遞到渲染層,通過 DOM diff 算法等渲染成最終頁面,所以需要盡量減少setData的使用以避免性能問題。
- 避免setData傳遞大量的新數(shù)據(jù)。數(shù)據(jù)的傳輸會經(jīng)歷跨線程傳輸和腳本編譯的過程,當數(shù)據(jù)量過大,會增加腳本編譯的執(zhí)行時間,占用 WebView JS 線程,從而影響到最終的渲染性能。