1. 程式人生 > 程式設計 >解析從小程式開發者工具原始碼看原理實現

解析從小程式開發者工具原始碼看原理實現

如何檢視小程式開發者工具原始碼

下面我們通過微信小程式開發者工具的原始碼來說說小程式的底層實現原理。以開發者工具版本號State v1.02.1904090的原始碼來窺探小程式的實現思路。如何檢視微信原始碼,對於mac使用者而言,檢視微信小程式開發者工具的包內容,然後進入Contents/Resources/app.nw/js/core/index.js,註釋掉如下程式碼就可以檢視開發者工具渲染後的程式碼。

// 開啟 inspect 視窗
if (nw.App.argv.indexOf('inspect') !== -1) {
    tools.openInspectWin()
}

然後重啟小程式開發者工具,就出現如下左側頁面,點選其中一個頁面就能看到view層的dom結構,如下圖右側。

解析從小程式開發者工具原始碼看原理實現

解析從小程式開發者工具原始碼看原理實現

小程式架構設計

1、小程式渲染是在同一個執行緒嗎?雙執行緒機制

開發過小程式的都知http://www.cppcns.com道,小程式是雙執行緒設計,即檢視渲染與業務邏輯分別在執行在不同的執行緒中。這個設計主要是解決web技術中的一個痛點:

web頁面開發渲染執行緒和指令碼執行緒是互斥的,長時間的指令碼執行可能會導致頁面失去響應或者白屏,體驗糟糕。

小程式為了更好體驗,將頁面的渲染執行緒和指令碼執行緒分開設計在不同執行緒中執行,具體實現:

  • 檢視view層在webview中渲染,一個頁面對應一個webview
  • 業務邏輯Appservice層執行在同一個JSCore執行緒中,具體ios是
    javascript
    Core,android是X5 JSCore,開發者工具是webview中;

這樣解決了長時間的指令碼阻塞頁面渲染的情況,但是也帶來一些新的問題:

  • 天生的延遲,執行緒間要通訊
  • 業務邏輯層因為執行在JSCore中無法訪問DOM和BOM的api;

開發者工具使用webview載入業務邏輯層的程式碼,雖然依賴的環境有DOM和BOM api,為了保持一致;小程式對所有的模組進行了區域性化處理使其不能訪問這些api。這樣雙執行緒通過native,開發者工具通過後臺websocket服務充當二者訊息中轉媒介,並且提供一些基礎功能。具體可以參考官網圖:

解析從小程式開發者工具原始碼看原理實現

2、小程式是webhttp://www.cppcns.com
渲染嗎?介面渲染機制

頁面渲染的方式主要有三種:

  • 純web渲染
  • 純native原生渲染
  • Hybrid渲染,即web和native渲染結合

因為小程式的宿主環境是微信,不太可能使用純native渲染,否則所有小程式需要跟微信一起編碼發版。採用純web渲染貌似是可行的,支援快速線上更新,通過加裝最新資源到本地即可渲染;但是純web渲染在一些有複雜互動的頁面上可能會面臨一些效能問題,這是因為在web技術中,UI渲染跟 javaScript 的指令碼執行都在一個單執行緒中執行,這就容易導致一些邏輯任務搶佔UI渲染的資源。所以小程式採用Hybrid方式渲染,用官網的描述如下:

介面主要由成熟的 Web 技術渲染,輔之以大量的介面提供豐富的客戶端原生能力。

同時,每個小程式頁面都是用不同的WebView去渲染,這樣可以提供更好的互動體驗,更貼近原生體驗,也避免了單個WebView的任務過於繁重。

既然採用Hybrid方式渲染,那麼頁面的渲染可能會用到原生native來渲染,什麼情況會用到原生渲染呢?

答案是使用到小程式提供的map、video、canvas、textarea等元件,頁面中原生渲染的渲染原理可以參考官網原生元件。但是在小程式開發者工具中原生元件是使用html標籤來模擬實現的。具體可以看下一節的map元件渲染結果。

3、小程式是用web的html標籤渲染嗎?Exparser元件框架

上面說到小程式主要由成熟的web技術渲染,能否www.cppcns.com直接使用html提供的標籤如div、table等組織頁面呢,答案不可以。主要考量:

  • 管控與安全:web技術可以通過指令碼獲取修改頁面敏感內容或者隨意跳轉其它頁面
  • 能力有限,會限制小程式的表現形式
  • 標籤眾多,增加理解成本

所以,小程式不能直接使用html標籤渲染頁面,其提供了10多個內建元件來收斂web標籤,並且提供一個JavaScript沙箱環境來避免js訪問任何瀏覽器api。

既然小程式不能直接使用html標籤來渲染頁面,那它提供的如view、cover-view等內建元件是否意味著最終都轉換為html提供的內建標籤來渲染呢?答案當不是。我們來看如下程式碼:

<view class="map-container">
  <map latitude='39.9088230000' style="height: 100%; width:100%;" longitude='116.3974700000' scale='16' id="id" bindregionchange="onRegionChange"></map>
  <view catchtap="onTap">test</view>
</view>

上面程式碼在開發者工具中最終渲染元素如下圖:

解析從小程式開發者工具原始碼看原理實現

可以看出,小程式提供的元件並沒有最終轉換為為html對應的標籤來渲染,而是使用自定義的元素來渲染。這些內建元件都是由Exparser框架負責管理,它內建在小程式基礎庫中,為小程式的各種元件提供基礎的支援。

Exparser框架基於Shadow DOM模型,模型上與WebComponents的ShadowDOM高度相似,具體可以參考官網元件系統。
內建元件的命名規範都是以wx-開頭的,外部引用內建元件如view,最終會呼叫底層的wx-view元件;Exparser的view元件建立方式如下:

解析從小程式開發者工具原始碼看原理實現

4、小程式可以操作dom嗎?資料驅動

小程式為了管控與安全,提供一個JavaScript沙箱環境來執行JavaScript程式碼,js程式碼不能訪問任何瀏覽器相關的介面,那就意味著js是不能操作dom和bom的,否則可能報錯。小程式實現沙箱環境呢?即通過將業務邏輯封裝到一個區域性環境中,區域性環境修改dom和bom的相關api指向。具體封裝形式如下:

解析從小程式開發者工具原始碼看原理實現

那麼問題來了,小程式是怎麼給業務程式碼加上以上封裝的呢?其實很簡單,在小程式開發者工具中有一個後臺服務,訪問小程式的每個模組的path時,後臺服務會呼叫wrapSourceCodeInDefine方法將請求的JS檔案的內容分別包裹在define域中,方法的程式碼如下圖所示:

解析從小程式開發者工具原始碼看原理實現

這裡的define是小程式底層實現模組化的方法之一,還有一個是require方法;通過define來定義一個模組,require來引用一個define定義的模組。從上面小程式對業務模組程式碼的封裝可以看出:

define定義的模組對傳遞了跟瀏覽器相關的介面同名的API,如window、document、localStroage等等
可能有人會說通過Function('return this')()來訪問全域性作用域window物件,但是小程式堵死了這條路,重寫了Function,eval重置為undefined。例如下圖:

解析從小程式開發者工具原始碼看原理實現

require在引用模組時只傳遞require、module、exports三個引數,那麼其他引數值就為undefined,不能在業務程式碼中訪問這些介面

可以看看require定義的原始碼:

解析從小程式開發者工具原始碼看原理實現

在實際的微信環境,業務邏輯層執行在JSCore中,其沒有瀏覽器相關的資訊,訪問dom無從談起;但是小程式開發者工具使用webview來執行業務邏輯程式碼,它有dom相關介面;所以通過上面沙箱環境來統一使js無法操作dom。

業務程式碼無法訪問dom,怎麼實現頁面動態更新呢?

答案就是採用類vue這種MVVM框架的資料驅動思想,即讓檢視狀態和檢視繫結在一起,狀態變更時,檢視也能自動變更,這樣就不用直接操作dom。

檢視的動態更新具體是採用virtual dom技術實現,virtual DOM相信大家都已有了解,大概是這麼個過程如下圖:

解析從小程式開發者工具原始碼看原理實現

實際處理可以簡單描述如下:

用JS物件模擬DOM樹 -> 比較兩棵虛擬DOM樹的差異 -> 把差異應用到真正的DOM樹上。

其中,virtual dom是通過內建的wcc可以將wxml轉換為js物件形式,以此來表示DOM樹結構。

下面以官網的一幅圖來說檢視動態更新的過程:

// wxml
 <view>{{msg}}</view>

// js
data: {
   msg: 'Hello World'
}

解析從小程式開發者工具原始碼看原理實現

上面說明了檢視如何更新的,其實在資料響應的過程中,還有最重要的一環,即業務邏輯層的如何將變化的資料同步到檢視層呢,這就涉及到雙執行緒的通訊了

5、小程式基礎庫作用到底是什麼?

我們在開發者工具開發小程式時,一般都會選擇一個基礎庫,如小程式開發者工具選擇介面:

解析從小程式開發者工具原始碼看原理實現

小程式基礎庫是用JavaScript寫的,但是我們並沒有在我們的小程式中直接引用,那麼我們是怎麼使用基礎庫提供功能的呢?答案是:

微信宿主環境會提前內建基礎庫,開啟小程式時會自動將基礎庫注入到小程式的檢視層和業務邏輯層中,小程式開發者工具則是由底層HTT程式設計客棧P服務負責注入。

下圖是小程式底層HTTP服務通過script指令碼注入的相關程式碼:

解析從小程式開發者工具原始碼看原理實現

小程式基礎庫功能包括兩個部分檢視層的WAWebview.js和業務邏輯層的WAService.js。下面就簡單說下對應功能:

WAService為業務邏輯層提供基礎功能

下看看一下WAService.js原始碼內容縮圖:

解析從小程式開發者工具原始碼看原理實現

從原始碼可以看出基礎庫提供的WAService.js有很多功能,主要包括以下幾部分

  • WeixinJSBridge:訊息通訊的統一封裝易於呼叫,主要微信環境與native,開發環境與開發者工具後臺服務的通訊。
  • wx: wx物件下面的api方法封裝
  • appServiceEngine:定義了全域性的方法如define,require,App,Page,Component,getApp,getCurrentPages等
  • virtualDOM: VirtualDOM,Diff和Render UI實現
  • expraser: expraser框架元件的方法定義,這意味著邏輯層也具有一定的元件樹組織能力。
  • Reporter: 小程式日誌元件

WAWebview為檢視層提供基礎功能

小程式基礎庫為檢視層提供的基礎功能有些與WAService相同,主要功能如下:

  • 訊息通訊封裝為WeixinJSBridge
  • 日誌元件Reporter封裝
  • wx物件下的api,跟WAService裡的不同的是其大部分都是處理UI顯示相關的方法
  • 小程式Expraser元件框架的實現和內建元件的註冊
  • VirtualDOM,Diff和Render UI實現
  • 定義頁面相關事件觸發

以上就是解析從小程式開發者工具原始碼看原理實現的詳細內容,更多關於從小程式開發者工具原始碼看原理實現的資料請關注我們其它http://www.cppcns.com相關文章!