Web内核微信小程序框架实践
作者:boxuechen,騰訊 WXG 客戶端開發(fā)工程師
背景
在正式進(jìn)入分享之前,簡(jiǎn)單介紹一下做這個(gè)小程序新框架的背景思路,主要目的有以下幾點(diǎn):
新框架和微信的主客戶端解耦,能夠獨(dú)立運(yùn)行,并且可以同時(shí)支持小程序和小游戲。
新框架能去擁抱更多的Web特性,深入到Chromium內(nèi)核中,去支持更多平臺(tái)。
還有一個(gè)目的就是,通過新框架去拓寬小程序生態(tài)的邊界,能夠在許多非移動(dòng)端設(shè)備,提供微信小程序生態(tài)。
1?小程序和PWA
首先來了解一下小程序和PWA,可以說小程序設(shè)計(jì)之初,還是吸收了很多Web特性,這也使得小程序和PWA應(yīng)用的用戶體驗(yàn)很接近,包括技術(shù)特點(diǎn)也有很多相似的地方。所以關(guān)于小程序和PWA的討論也非常多,接下來會(huì)從框架的角度入手,來對(duì)比一下小程序和PWA的相關(guān)特點(diǎn)。
1.1 小程序框架簡(jiǎn)介
上面這個(gè)圖,是移動(dòng)端小程序的框架,它主要由三部分組成,分別是視圖層,邏輯層和JS綁定。其中:
視圖層,用來展示用戶的UI,通過Web內(nèi)核的WebPage來進(jìn)行展示。?
邏輯層,運(yùn)行開發(fā)者代碼,在一個(gè)獨(dú)立的JS線程環(huán)境中,這個(gè)JS環(huán)境在Android是獨(dú)立的V8提供的,iOS上是JSCore 。
JSAPI,都是通過JS綁定,將平臺(tái)和微信相關(guān)的能力暴露給邏輯層,從而給開發(fā)者提供相應(yīng)的JSAPI接口。
setData,傳輸視圖層和邏輯層的數(shù)據(jù)。
1.2 PWA簡(jiǎn)介
上面這個(gè)圖,是一個(gè)PWA架構(gòu)的簡(jiǎn)圖,在這里我們可以看到:
PWA的運(yùn)行環(huán)境,是一個(gè)完整的Web內(nèi)核。
index.html:類似小程序的視圖層,運(yùn)行在Web內(nèi)核的Render線程,可以執(zhí)行JS。
sw.js:類似小程序的邏輯層,運(yùn)行在Web內(nèi)核的ServiceWorker線程,管理PWA的生命周期。
JSAPI:Web內(nèi)核通過WebIDL綁定的方式,將底層能力暴露出來。
postMessage:用于給ServiceWorker線程和Render線程提供通信能力,性能非常好,支持Transferable 對(duì)象傳輸。
1.3?小程序和PWA對(duì)比
通過上文的介紹,可以看到小程序和PWA的主體上都是雙線程架構(gòu)的模型,但是有區(qū)別的是:
視圖層:PWA可以執(zhí)行JSAPI,小程序不行。
邏輯層:PWA的JSRuntime是Web內(nèi)核提供的,不再需要獨(dú)立的V8。
JS綁定: PWA的接口都是標(biāo)準(zhǔn)的H5能力,通過WebIDL綁定,小程序是微信相關(guān)接口能力,獨(dú)立綁定。
另外就是,PWA的應(yīng)用開發(fā)者擁有完全的代碼控制能力,但是小程序?yàn)榱吮Wo(hù)微信相關(guān)數(shù)據(jù)安全,都會(huì)讓開發(fā)者代碼運(yùn)行在一個(gè)沙箱環(huán)境上。
1.4?向PWA學(xué)習(xí)
PWA的優(yōu)點(diǎn)非常多,非常值得小程序?qū)W習(xí),其中有幾點(diǎn):
Service Worker線程,非常輕量,不再需要額外的V8環(huán)境。
PostMessage:支持結(jié)構(gòu)化Clone和Transable對(duì)象傳輸,性能非常高。
PWA還擁有非常豐富的H5 API,比如Canvas、WebSocket等。
PWA擁有Chrome Devtools的Inspector能力,非常方便進(jìn)行調(diào)試。
這些特點(diǎn)都能提高整個(gè)小程序的運(yùn)行性能,同時(shí)還能降低框架的開發(fā)維護(hù)成本。所以在設(shè)計(jì)新的小程序框架的時(shí)候,我們希望能夠吸收PWA的優(yōu)點(diǎn),同時(shí)能夠靈活滿足自己業(yè)務(wù)的需要。這樣就需要深入Web內(nèi)核中做一些修改和定制,來實(shí)現(xiàn)一個(gè)符合業(yè)務(wù)需求的小程序框架。
2.?基于Chromium內(nèi)核的小程序框架
2.1 整體框架
先來看一下新框架最終的架構(gòu)圖,這個(gè)圖其實(shí)和PWA的架構(gòu)很接近,運(yùn)行環(huán)境是在Chromium內(nèi)核的基礎(chǔ)上。
視圖層:是Chromium內(nèi)核的Render線程,用來渲染Page頁(yè)面
邏輯層:是修改Chromium內(nèi)核,實(shí)現(xiàn)自定義的XWeb Worker線程,這樣就不再需要額外的V8了。
JSAPI:通過給邏輯層,集成Node,使得JSAPI 能力由 H5 API和 Node API 以及 Node擴(kuò)展接口支持.
PostMessage:?由于是基于Chromium內(nèi)核的Worker線程,所以關(guān)于數(shù)據(jù)通信這塊,很自然的也會(huì)擁有PostMessage的能力,來替代原有的setData。
這里修改Chromium內(nèi)核,是存在幾個(gè)挑戰(zhàn)點(diǎn)的:
如何在Chromium中增加自定義的Web Worker線程,作為小程序的邏輯層。
如何給Chromium集成Node,用來擴(kuò)展小程序的JSAPI。
如何給Web Worker增加沙箱能力,確保微信環(huán)境的數(shù)據(jù)安全。
接下來將詳細(xì)介紹,我們?nèi)绾谓鉀Q這些問題的。
2.2?自定義XWeb?Worker線程
那么如何設(shè)計(jì)XWeb Worker的線程,從以下兩個(gè)分析點(diǎn)入手:
分析Chromium中Web Workers實(shí)現(xiàn)類。
分析Chromium中Web Workers的創(chuàng)建流程。
這里需要解決的核心問題包括兩個(gè),一個(gè)是運(yùn)行環(huán)境問題、另一個(gè)就是運(yùn)行線程的問題。
通過上圖可以看到所有的Web workers的運(yùn)行環(huán)境實(shí)現(xiàn)其父類都是 WorkerGlobalScope 對(duì)象,提供了基本的JS運(yùn)行環(huán)境, 因此我們XWebWorker運(yùn)行環(huán)境的實(shí)現(xiàn)同樣繼承自該類。
然后我們參考Chromium中關(guān)于SharedWorker的實(shí)現(xiàn),設(shè)計(jì)了我們XWebWorker。
通過上面這個(gè)流程圖,可以看到:
WorkerGlobalScope對(duì)象的創(chuàng)建流程首先是由JS在Render進(jìn)程發(fā)起。
接著調(diào)到Browser進(jìn)程,在Browser進(jìn)程完成加載資源、構(gòu)建依賴對(duì)象等。
接下來創(chuàng)建請(qǐng)求會(huì)再次回到Render進(jìn)程進(jìn)行真正的創(chuàng)建,這時(shí)就會(huì)創(chuàng)建Worker線程以及WorkerGlobalScope對(duì)象。
參考上面這個(gè)流程,實(shí)現(xiàn)自定義的XWeb Worker,這樣就可以共享Chromium內(nèi)核的V8,不再需要額外JS運(yùn)行環(huán)境。同時(shí)我們可以復(fù)用更多的H5 API的接口能力,以及PostMessage的高性能通信能力。
2.3?集成Node,支持JSAPI
由于Chromium內(nèi)核本身沒有提供JS綁定的能力,這樣也不方便我們靈活擴(kuò)展JS接口,所以在這里參考了electron的思路,將node融合Chromium內(nèi)核中。
這里將Node融合到Chromium內(nèi)核,最大的困難在于消息循環(huán)的融合:
Node.js 事件循環(huán)基于 libuv,而Chromium 基于自己實(shí)現(xiàn)的 message_loop。
Worker線程只能運(yùn)行一個(gè)消息循環(huán)。
electron非常巧妙的利用epoll事件機(jī)制,通過獨(dú)立線程輪詢backend fd,將libuv融入Chromium的message loop,從而避免了大量修改Chromium內(nèi)核的代碼。
大家如果對(duì)這個(gè)原理感興趣,可以深入閱讀一下《Electron Internals: Message Loop Integration》這篇文章。
成功融合Node之后的Chromium內(nèi)核,將共用同一個(gè)V8,而且在邏輯端,也會(huì)擁有非常豐富的JSAPI 能力:
在XWeb Worker中可以使用豐富的H5 API能力。比如PostMessage,WebSocket還有offscreen Canvas 等。
同時(shí)我們也會(huì)擁有Node相關(guān)的API能力,比如file的讀寫 http相關(guān)接口等。
如果業(yè)務(wù)有需求變更的話,可以通過?node 的 addon模塊來增加我們自己的JSAPI能力
這樣下來,就能在自定義XWeb Worker線程的基礎(chǔ)上,實(shí)現(xiàn)JSAPI的能力支持。
2.4?沙箱能力支持
小程序還有一個(gè)非常重要的能力,就是沙箱能力,這個(gè)主要是確保開發(fā)者代碼運(yùn)行在一個(gè)受到控制的安全環(huán)境中。
2.4.1?挑戰(zhàn)點(diǎn)
這里的挑戰(zhàn)點(diǎn)在于:
WebWorker 沒有提供創(chuàng)建沙箱函數(shù)的相關(guān)的API。
Node VM創(chuàng)建裸V8 context,無法處理Chromium內(nèi)核的JS異常及特定JS函數(shù)。
這也使得Electron團(tuán)隊(duì)已在Renderer進(jìn)程中禁用了Node VM。
2.4.2?V8::Context的分析
所以在這里,就必須深入分析Chromium內(nèi)核對(duì)V8:Context的封裝,只有深入了解后,才能封裝出所需要的沙箱接口。
在Chromium下有兩種類型的JS執(zhí)行環(huán)境:一種是iframe類型、另外一種是worker類型:
對(duì)于iframe來說,在blink層對(duì)應(yīng)的是Document對(duì)象,其繼承自 Context 類,它提供了JS腳本執(zhí)行環(huán)境的公共屬性。Document對(duì)象通過 Context類、 ScriptState類、ScriptState類,完成了對(duì)裸v8 context的封裝。
同樣的對(duì)于ServiceWorker端,也是基于類似的流程完成了對(duì)裸v8 context的包裝。其中 WorkerGlocalScope,則提供了滿足JS運(yùn)行的基本口實(shí)現(xiàn)。
2.4.3 實(shí)現(xiàn)XWeb Worker 的沙箱接口
在這里借鑒Chromium內(nèi)核中Web Worker創(chuàng)建Context的思路:
創(chuàng)建VMWorkerGlobalScope,用于創(chuàng)建context及ScriptState等關(guān)聯(lián)對(duì)象。
VM Context的Js 通過調(diào)用父類的封裝對(duì)象調(diào)用執(zhí)行。
對(duì)外暴露CreateVMContext等API接口,從而提供沙箱能力支持。
3.基于Chromium內(nèi)核的小游戲框架
基于Chromium內(nèi)核的小游戲框架,相比小程序而言就簡(jiǎn)潔很多了,它同樣運(yùn)行在Chromium內(nèi)核中,只會(huì)使用Render線程,作為小游戲的JS線程。然后會(huì)使用 H5 的接口,比如canvas webgl,同樣也會(huì)融合 node 進(jìn)來,來做JS API的擴(kuò)展。
3.1 JSAPI能力支持
小游戲的JS API,由于是運(yùn)行在Chromium的Render線程上,能夠直接使用 很多 H5的能力,比如Canvas、WebGL、WebAudio 等。同樣融入Node后,會(huì)獲得node相應(yīng)的api能力,比如node 的 file http socket 等。如果業(yè)務(wù)有擴(kuò)展,比如要新增jsapi接口,可以通過 node addon 來增加實(shí)現(xiàn)。
3.2 沙箱能力支持
小游戲的沙箱能力,我們這里直接使用iframe,來作為獨(dú)立的js context,當(dāng)然考慮到安全,我們會(huì)在Chromium內(nèi)核中,禁用iframe中的DOM和BOM相關(guān)接口。
4.進(jìn)程模型&跨平臺(tái)實(shí)踐
4.1 進(jìn)程模型
如下圖所示,左側(cè)是Host進(jìn)程,右邊是Chromium的多進(jìn)程模型:
每一個(gè)小程序和小游戲都是一個(gè)獨(dú)立的Render進(jìn)程 。
Host進(jìn)程,比如PC微信,小程序一些微信相關(guān)的能力,需要和Host進(jìn)程打交道。
在這里,我們通過IPC通信的方式,來降低小程序和Host進(jìn)程的耦合程度。
而通過Chromium的多進(jìn)程模型,來保證每個(gè)小程序和小游戲 以及Host之間互相不影響。
4.2 跨平臺(tái)SDK
得益與Chromium的架構(gòu)和跨平臺(tái)能力,也使得我們小程序框架非常容易移植和集成。
多個(gè)平臺(tái),使用同一份源碼
SDK可獨(dú)立使用
PC微信集成
4.3 Windows 和 Linux 平臺(tái)演示
1.集成到Windows微信,從桌面拉起小程序。
2.獨(dú)立的SDK Demo運(yùn)行在Linux平臺(tái)。
4.4 移植到 Android Native 環(huán)境
基于新框架的跨平臺(tái)特性,這里嘗試將新的小程序框架移植到Android的Native環(huán)境,不會(huì)依賴任何的Android Java框架代碼:
Chromium內(nèi)核只會(huì)對(duì)接 Android Native框架,最重要的就是圖形和輸入。
通過封裝 Android Native 圖形系統(tǒng)SurfaceFlinger和輸入系統(tǒng)Input Manager的 C 接口,對(duì)接Chromium Ozone。
其實(shí)移植到Android Native后的整體架構(gòu),和【曾經(jīng)的FirefoxOS】以及【現(xiàn)在的KaiOS】架構(gòu)比較接近,沒有任何Java代碼,只有Web的技術(shù)棧。下面通過一個(gè)Demo視頻,看看如何在pixel3手機(jī)上開機(jī)到一個(gè)【小程序列表】,并且運(yùn)行【小程序app】:
5 總結(jié)與展望
Chromium可以說是目前終端領(lǐng)域最復(fù)雜的軟件之一了,而小程序也是目前前端最火的領(lǐng)域。本文給出了一個(gè)全新的小程序框架思路,基于Chromium內(nèi)核,進(jìn)行深度定制,復(fù)用H5生態(tài),靈活滿足小程序的業(yè)務(wù)需求,不僅能減少大量的開發(fā)成本,而且性能指標(biāo)也有大幅提升。
而我們?cè)趯?shí)際性能評(píng)測(cè)中,新框架相比過去的框架方案,小程序首屏顯示,冷啟動(dòng)耗時(shí)減少70%,用戶體驗(yàn)大幅度改善。而且由于減少了邏輯端的V8,以及大量JSAPI 復(fù)用H5的能力,使得內(nèi)存占用方面,也有顯著減少。
而Chromium的跨平臺(tái)能力,也使得整個(gè)框架,非常方便移植到各個(gè)平臺(tái),無論是Windows,Linux,Mac還是Android,甚至類似WebOS的架構(gòu),新框架都可以輕松適應(yīng)。
最后,希望這篇文章能給到大家一些新的思路。
參考資料
https://www.chromium.org/Home
https://developer.mozilla.org/zh-CN/docs/Web/Progressive_web_apps
https://www.electronjs.org/blog/electron-internals-node-integration
https://github.com/electron/electron
https://chromium.googlesource.com/chromium/src/+/HEAD/docs/ozone_overview.md
https://zh.wikipedia.org/wiki/Firefox_OS
https://developer.kaiostech.com/docs/sfp-3.0/
總結(jié)
以上是生活随笔為你收集整理的Web内核微信小程序框架实践的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Golang 简洁架构实战
- 下一篇: 技术她力量,鹅厂女博士的寻“豹”之旅