對于小程序框架實現原理,在支付寶小程序官方文檔上有這樣一段描述:
與傳統的 H5 應用不同,小程序運行架構分為 webview 和 worker 兩個部分。webview 負責渲染,worker 則負責存儲數據和執行業務邏輯。 1.webview 和 worker 之間的通信是異步的。這意味著當我們調用 setData 時,我們的數據并不會立即渲染,而是需要從 worker 異步傳輸到 webview。 2.數據傳輸時需要序列化為字符串,然后通過 evaluateJavascript 方式傳輸,數據大小會影響性能。
概括一下,大致意思是小程序框架核心是通過2個線程來完成的,主線程負責webView的渲染工作,worker線程負責js執行。說到這里,你是不是會產生一個疑問:為什么多線程通信損耗性能還要搞多線程呢?可能大多數人都知道因為Web技術實在是太開放了,開發者可以為所欲為。這種情況在小程序中是不允許的,不允許使用
但是卻始終無法解決一個問題:如何防止開發者做一些我們想禁用的功能。因為是一個網頁,開發者可以執行JS,可以操作DOM,可以操作BOM,可以做一切事情。so,我們需要一個沙箱環境,來運行我們的js,這個沙箱環境需要可以屏蔽掉所有的危險動作。說了這么多,大致想法如下:
關于UI層的渲染,有很多實現方式,比如通過類似VNode -> diff的自定義渲染方式來實現了一個簡易的小程序框架:
function App (props) { const {msg} = props; return () => (
核心就是通過定義@babel/plugin-transform-react-jsx插件來轉換 jsx,生成Vnode,再交給Worker通過Diff,最后通過worker postmsg 來通知渲染進程更新:
let index = 0;// 得到diff差異let diffData = diff(0, index, oldVnode, newVnode);// 通知渲染進程更新self.postMessage(JSON.stringify(diffData)); 復制代碼
有點麻煩?能不能繼承現有框架能力?比如Vue、React。當然可以,我們下面就來介紹基于Vue來實現的demo.
實現一個基于Vue的小程序框架
有了上面的知識,我們先不著急寫代碼,先來捋一下我們需要什么,首先我們需要實現這樣一個能力:渲染層和邏輯層分離,emmm。。。大致我們的小程序是這樣的
// page.js 邏輯層export default { data: { msg: 'hello Vox', }, create() { console.log(window); setTimeout(() => { this.setData({ msg: 'setData', }) }, 1000); },}復制代碼// page.vxml.js 渲染層export default () => { return '
這里的渲染層為啥不是類似于微信或者支付寶小程序 wxml,axml這樣的呢?當然可以,其實我只是為了方便而已,我們可以手寫一個webpack loader 來處理一下我們自定義的文件。這里有興趣的小伙伴可以嘗試一下。不是本次介紹的核心。
好了,上面是我們想要的功能,我們核心是框架,框架層要干的事核心有2個:構造worker初始化引擎;構造渲染引擎。
// index.worker.js 構造workerconst voxWorker = options => { const {config} = options; // Vue生命周期收集 const lifeCircleMap = { 'lifeCircle:create': [config.create], }; // 定義setData方法用于通知UI層渲染更新 self.setData = (data) => { console.log('setData called'); self.postMessage( JSON.stringify({ type: 'update', data, }) , null ); }; // worker構建完成,通知渲染層初始化 self.postMessage( JSON.stringify({ type: 'init', data: config.data, }) , null ); // 執行生命周期函數 self.onmessage = e => { const {type} = JSON.parse(e.data); lifeCircleMap[type].forEach(lifeCircle => lifeCircle.call(self)) }}export default voxWorker;復制代碼
上面代碼核心干的事其實并不復雜,也就是:
收集需要用到的生命周期
定義setData函數,提供給用戶層更新UI
定義監聽函數,處理生命周期函數執行
通知UI進程開啟渲染。
當我們通知UI進程開始渲染的時候,UI進程也就是需要構造Vue實例,進行頁面render:
worker.onmessage = e => { const {type, data} = JSON.parse(e.data); if (type === 'init') { const mountNode = document.createElement('div'); document.body.appendChild(mountNode); target = new Vue({ el: mountNode, data: () => data, template: template(), created(){ worker.postMessage(JSON.stringify({ type: 'lifeCircle:create', }) , null); } }); } }復制代碼
可以看到UI線程在初始化的時候,一并初始化了 worker層傳遞來的data,并對生命周期進行了聲明。當生命周期函數在UI層觸發的時候,會通知 worker。在我們的例子中,create 鉤子通過setData 進行了一個更新data的動作。我們知道 setData 就是拿到數據進行通知更新:
// UI線程接收到通知消息,更新UI if (type === 'update') { Object.keys(data).map(key => { target[key] = data[key]; }); }復制代碼
說到這里,似乎一切都感覺清楚多了,我們用worker執行用戶js邏輯,worker內無法操作dom,無法訪問window。當我們需要更新dom,可以去通知渲染線程做更新。我們也很容易想到不斷地數據傳輸對性能的損耗,所以我們當然可以做進一步的優化,多個setData可以組合一起發送?就是建立一個通信。其次再看一些,我們的worker再通信傳輸數據的過程中不斷通過字符串的parse和stringify
綠色部分時原生JSON.stringify(), 關于這一塊如何提升性能一方面可以通過減少數據傳輸量,其他的優化也可以參考這里如何提升JSON.stringify()的性能
最后你可能會問,小程序用法都是
的語法糖,寫一個 vue組件,組件名稱叫view是不是就可以了呢?
這里為大家介紹的也只是小程序的冰山一角,內部的容器開放jsBridge能力,離線機制,跨webview通信機制等等有興趣的可以去探索,當然這里也只是拋磚引玉。
有興趣的可以查看源碼Vox
結語
引用知乎上的一段話:
其實,大家對小程序的底層實現都是使用雙線程模型,大家對外宣稱都會說是為了:方便多個頁面之間數據共享和交互為native開發者提供更好的編碼體驗為了性能(防止用戶的JS執行卡住UI線程)其他好處但其實真正的原因其實是:“安全”和“管控”,其他原因都是附加上去的。因為Web技術是非常開放的,JavaScript可以做任何事。但在小程序這個場景下,它不會給開發者那么高的權限:不允許開發者把頁面跳轉到其他在線網頁不允許開發者直接訪問DOM不允許開發者隨意使用window上的某些未知的可能有危險的API,當然,想解決這些問題不一定非要使用雙線程模型,但雙線程模型無疑是最合適的技術方案。
經過上面的介紹,是不是發現小程序其實也就那么回事,并沒有多么....這邊文章主要希望能讓你對經常使用的框架有一個原理性的初步認識,至少我們再用的時候可以規避掉一些坑,或者性能問題。 關注 文章 瀏覽量 關注 文章 瀏覽量 關注 文章 瀏覽量
發布評論請先 登錄
相關推薦
OpenHarmony程序分析框架論文入選ICSE 2025
![OpenHarmony<b class='flag-5'>程序</b>分析<b class='flag-5'>框架</b>論文入選ICSE 2025](https://file1.elecfans.com/web3/M00/04/96/wKgZO2d2KGCAUcJVAAAk9btwCMQ998.png)
SSM框架的源碼解析與理解
SSM框架在Java開發中的應用 如何使用SSM進行web開發
JavaWeb框架比較
Dubbo源碼淺析(一)—RPC框架與Dubbo
![Dubbo源碼淺析(<b class='flag-5'>一</b>)—RPC<b class='flag-5'>框架</b>與Dubbo](https://file1.elecfans.com//web2/M00/02/FD/wKgZoma-_SiAdj3eAAA-aRPaYY0047.png)
日志框架簡介-Slf4j+Logback入門實踐
![日志<b class='flag-5'>框架</b>簡介-Slf4j+Logback入門實踐](https://file1.elecfans.com//web2/M00/FF/2D/wKgZomandamADVaeAADrIxKpkeI928.jpg)
評論