淺談小程序運行機制

寫做背景

接觸小程序有一段時間了,總得來講小程序開發門檻比較低,但其中基本的運行機制和原理仍是要懂的。「好比我在面試的時候問到一個關於小程序的問題,問小程序有window對象嗎?他說有吧」,但實際上是沒有的。感受他並無瞭解小程序底層的一些東西,歸根結底來講應該只能算會使用這個工具,但並不明白其中的道理。html

小程序與普通網頁開發是有很大差異的,這就要從它的技術架構底層去剖析了。還有好比習慣Vue,react開發的開發者會吐槽小程序新建頁面的繁瑣,page必須由多個文件組成、組件化支持不完善、每次更改 data 裏的數據都得setData、沒有像Vue方便的watch監聽、不能操做Dom,對於複雜性場景不太好,以前不支持npm,不支持sass,less預編譯處理語言。react

「有的人說小程序就像被閹割的Vue」,哈哈固然了,他們從設計的出發點就不一樣,咱也得理解小程序設計的初衷,經過它的使用場景,它爲何採用這種技術架構,這種技術架構有什麼好處,相信在你瞭解完這些以後,就會理解了。下面我會從如下幾個角度去分析小程序的運行機制和它的總體技術架構。web

瞭解小程序的由來

在小程序沒有出來以前,最初微信WebView逐漸成爲移動web重要入口,微信發佈了一整套網頁開發工具包,稱之爲 JS-SDK,給全部的 Web 開發者打開了一扇全新的窗戶,讓全部開發者均可以使用到微信的原生能力,去完成一些以前作不到或者難以作到的事情。面試

但JS-SDK 的模式並無解決使用移動網頁遇到的體驗不良的問題,好比受限於設備性能和網絡速度,會出現白屏的可能。所以又設計了一個加強版JS-SDK,也就是「微信 Web 資源離線存儲」,但在複雜的頁面上依然會出現白屏的問題,緣由表如今頁面切換的生硬和點擊的遲滯感。這個時候須要一個 JS-SDK 所處理不了的,使用戶體驗更好的一個系統,小程序應運而生。npm

  • 快速的加載
  • 更強大的能力
  • 原生的體驗
  • 易用且安全的微信數據開放
  • 高效和簡單的開發

小程序與普通網頁開發的區別

小程序的開發同普通的網頁開發相比有很大的類似性,小程序的主要開發語言也是 JavaScript,可是兩者仍是有些差異的。編程

  • 普通網頁開發可使用各類瀏覽器提供的 DOM API,進行 DOM 操做,小程序的邏輯層和渲染層是分開的,邏輯層運行在 JSCore
    中,並無一個完整瀏覽器對象,於是缺乏相關的DOM API和BOM
    API。
  • 普通網頁開發渲染線程和腳本線程是互斥的,這也是爲何長時間的腳本運行可能會致使頁面失去響應,而在小程序中,兩者是分開的,分別運行在不一樣的線程中。
  • 網頁開發者在開發網頁的時候,只須要使用到瀏覽器,而且搭配上一些輔助工具或者編輯器便可。小程序的開發則有所不一樣,須要通過申請小程序賬號、安裝小程序開發者工具、配置項目等等過程方可完成。
  • 小程序的執行環境

clipboard.png

小程序架構

1、技術選型

通常來講,渲染界面的技術有三種:小程序

  • 用純客戶端原生技術來渲染
  • 用純 Web 技術來渲染
  • 用客戶端原生技術與 Web 技術結合的混合技術(簡稱 Hybrid 技術)來渲染

經過如下幾個方面分析,小程序採用哪一種技術方案微信小程序

  • 開發門檻:Web 門檻低,Native 也有像 RN 這樣的框架支持
  • 體驗:Native 體驗比 Web 要好太多,Hybrid 在必定程度上比 Web 接近原生體驗
  • 版本更新:Web 支持在線更新,Native 則須要打包到微信一塊兒審覈發佈
  • 管控和安全:Web 可跳轉或是改變頁面內容,存在一些不可控因素和安全風險

因爲小程序的宿主環境是微信,若是用純客戶端原生技術來編寫小程序,那麼小程序代碼每次都須要與微信代碼一塊兒發版,這種方式確定是不行的。設計模式

因此須要像web技術那樣,有一份隨時可更新的資源包放在雲端,經過下載到本地,動態執行後便可渲染出界面。若是用純web技術來渲染小程序,在一些複雜的交互上可能會面臨一些性能問題,這是由於在web技術中,UI渲染跟JavaScript的腳本執行都在一個單線程中執行,這就容易致使一些邏輯任務搶佔UI渲染的資源。api

因此最終採用了二者結合起來的Hybrid 技術來渲染小程序,能夠用一種近似web的方式來開發,而且能夠實如今線更新代碼,同時引入組件也有如下好處:

  • 擴展 Web 的能力。好比像輸入框組件(input, textarea)有更好地控制鍵盤的能力
  • 體驗更好,同時也減輕 WebView 的渲染工做
  • 繞過 setData、數據通訊和重渲染流程,使渲染性能更好
  • 用客戶端原生渲染內置一些複雜組件,能夠提供更好的性能

2、雙線程模型

小程序的渲染層和邏輯層分別由 2 個線程管理:視圖層的界面使用了 WebView 進行渲染,邏輯層採用 JsCore 線程運行 JS腳本。

圖片描述

圖片描述

那麼爲何要這樣設計呢,前面也提到了管控和安全,爲了解決這些問題,咱們須要阻止開發者使用一些,例如瀏覽器的window對象,跳轉頁面、操做DOM、動態執行腳本的開放性接口。

咱們可使用客戶端系統的 JavaScript 引擎,iOS 下的 JavaScriptCore 框架,安卓下騰訊 x5 內核提供的 JsCore 環境。

這個沙箱環境只提供純 JavaScript 的解釋執行環境,沒有任何瀏覽器相關接口。

這就是小程序雙線程模型的由來:

  • 邏輯層:建立一個單獨的線程去執行 JavaScript,在這裏執行的都是有關小程序業務邏輯的代碼,負責邏輯處理、數據請求、接口調用等
  • 視圖層:界面渲染相關的任務全都在 WebView 線程裏執行,經過邏輯層代碼去控制渲染哪些界面。一個小程序存在多個界面,因此視圖層存在多個 WebView 線程
  • JSBridge 起到架起上層開發與Native(系統層)的橋樑,使得小程序可經過API使用原生的功能,且部分組件爲原生組件實現,從而有良好體驗

3、雙線程通訊

把開發者的 JS 邏輯代碼放到單獨的線程去運行,但在 Webview 線程裏,開發者就無法直接操做 DOM。

那要怎麼去實現動態更改界面呢?

如上圖所示,邏輯層和試圖層的通訊會由 Native (微信客戶端)作中轉,邏輯層發送網絡請求也經由 Native 轉發。

這也就是說,咱們能夠把 DOM 的更新經過簡單的數據通訊來實現。

Virtual DOM 相信你們都已有了解,大概是這麼個過程:用 JS 對象模擬 DOM 樹 -> 比較兩棵虛擬 DOM 樹的差別 -> 把差別應用到真正的 DOM 樹上。

如圖所示:

clipboard.png

1. 在渲染層把 WXML 轉化成對應的 JS 對象。

2. 在邏輯層發生數據變動的時候,經過宿主環境提供的 setData 方法把數據從邏輯層傳遞到 Native,再轉發到渲染層。

3. 通過對比先後差別,把差別應用在原來的 DOM 樹上,更新界面。

咱們經過把 WXML 轉化爲數據,經過 Native 進行轉發,來實現邏輯層和渲染層的交互和通訊。

而這樣一個完整的框架,離不開小程序的基礎庫。

4、小程序的基礎庫

小程序的基礎庫能夠被注入到視圖層和邏輯層運行,主要用於如下幾個方面:

  • 在視圖層,提供各種組件來組建界面的元素
  • 在邏輯層,提供各種 API 來處理各類邏輯
  • 處理數據綁定、組件系統、事件系統、通訊系統等一系列框架邏輯

因爲小程序的渲染層和邏輯層是兩個線程管理,兩個線程各自注入了基礎庫。

小程序的基礎庫不會被打包在某個小程序的代碼包裏邊,它會被提早內置在微信客戶端。

這樣能夠:

  • 下降業務小程序的代碼包大小
  • 能夠單獨修復基礎庫中的 Bug,無需修改到業務小程序的代碼包

5、Exparser 框架

Exparser是微信小程序的組件組織框架,內置在小程序基礎庫中,爲小程序的各類組件提供基礎的支持。小程序內的全部組件,包括內置組件和自定義組件,都由Exparser組織管理。

Exparser的主要特色包括如下幾點:

  1. 基於Shadow
    DOM模型:模型上與WebComponents的ShadowDOM高度類似,但不依賴瀏覽器的原生支持,也沒有其餘依賴庫;實現時,還針對性地增長了其餘API以支持小程序組件編程。
  2. 可在純JS環境中運行:這意味着邏輯層也具備必定的組件樹組織能力。
  3. 高效輕量:性能表現好,在組件實例極多的環境下表現尤爲優異,同時代碼尺寸也較小。
小程序中,全部節點樹相關的操做都依賴於Exparser,包括WXML到頁面最終節點樹的構建、createSelectorQuery調用和自定義組件特性等。

內置組件

基於Exparser框架,小程序內置了一套組件,提供了視圖容器類、表單類、導航類、媒體類、開放類等幾十種組件。有了這麼豐富的組件,再配合WXSS,能夠搭建出任何效果的界面。在功能層面上,也知足絕大部分需求。

6、運行機制

小程序啓動會有兩種狀況,一種是「冷啓動」,一種是「熱啓動」。假如用戶已經打開過某小程序,而後在必定時間內再次打開該小程序,此時無需從新啓動,只需將後臺狀態的小程序切換到前臺,這個過程就是熱啓動;冷啓動指的是用戶首次打開或小程序被微信主動銷燬後再次打開的狀況,此時小程序須要從新加載啓動。
  • 小程序沒有重啓的概念
  • 當小程序進入後臺,客戶端會維持一段時間的運行狀態,超過必定時間後(目前是5分鐘)會被微信主動銷燬
  • 當短期內(5s)連續收到兩次以上收到系統內存告警,會進行小程序的銷燬

clipboard.png

7、更新機制

小程序冷啓動時若是發現有新版本,將會異步下載新版本的代碼包,並同時用客戶端本地的包進行啓動,即新版本的小程序須要等下一次冷啓動纔會應用上。 若是須要立刻應用最新版本,可使用 wx.getUpdateManager API 進行處理。

8、性能優化

主要的優化策略能夠概括爲三點:

  • 精簡代碼,下降WXML結構和JS代碼的複雜性;
  • 合理使用setData調用,減小setData次數和數據量;
  • 必要時使用分包優化。

一、setData 工做原理

小程序的視圖層目前使用 WebView 做爲渲染載體,而邏輯層是由獨立的 JavascriptCore 做爲運行環境。在架構上,WebView 和 JavascriptCore 都是獨立的模塊,並不具有數據直接共享的通道。當前,視圖層和邏輯層的數據傳輸,實際上經過兩邊提供的 evaluateJavascript 所實現。即用戶傳輸的數據,須要將其轉換爲字符串形式傳遞,同時把轉換後的數據內容拼接成一份 JS 腳本,再經過執行 JS 腳本的形式傳遞到兩邊獨立環境。

而 evaluateJavascript 的執行會受不少方面的影響,數據到達視圖層並非實時的。

二、常見的 setData 操做錯誤

  1. 頻繁的去 setData在咱們分析過的一些案例裏,部分小程序會很是頻繁(毫秒級)的去setData,其致使了兩個後果:Android下用戶在滑動時會感受到卡頓,操做反饋延遲嚴重,由於 JS 線程一直在編譯執行渲染,未能及時將用戶操做事件傳遞到邏輯層,邏輯層亦沒法及時將操做處理結果及時傳遞到視圖層;渲染有出現延時,因爲 WebView 的 JS 線程一直處於忙碌狀態,邏輯層到頁面層的通訊耗時上升,視圖層收到的數據消息時距離發出時間已通過去了幾百毫秒,渲染的結果並不實時;
  2. 每次 setData 都傳遞大量新數據由setData的底層實現可知,咱們的數據傳輸實際是一次 evaluateJavascript
  3. 腳本過程,當數據量過大時會增長腳本的編譯執行時間,佔用 WebView JS 線程, 後臺態頁面進行
    setData當頁面進入後臺態(用戶不可見),不該該繼續去進行setData,後臺態頁面的渲染用戶是沒法感覺的,另外後臺態頁面去setData也會搶佔前臺頁面的執行。

總結

大體從以上幾個角度分析了小程序的底層架構,從小程序的由來、到雙線程的出現、設計、通訊、到基礎庫、Exparser 框架、再到運行機制、性能優化等等,都是一個個相關而又相互影響的選擇。關於小程序的底層框架設計,其實涉及到的還有不少,好比自定義組件,原生組件、性能優化等方面,都不是一點能講完的,還要多看源碼,多思考。每個框架的誕生都有其意義,咱們做爲開發者能作的不僅是會使用這個工具,還應理解它的設計模式。只有這樣纔不會被工具左右,才能走的更遠!

相關文章
相關標籤/搜索