運行環(huán)境與網(wǎng)頁的區(qū)別
網(wǎng)頁開發(fā)渲染線程和JS線程是互斥的,渲染線程和JS線程都可以操作DOM,容易引起混亂,當執(zhí)行 JS 引擎線程時,GUI渲染線程會被掛起,當前任務(wù)隊列為空時,JS引擎才會去執(zhí)行 GUI 渲染。這也是為什么長時間的腳本運行可能會導致頁面失去響應(yīng)的原因。
而對于小程序,它的邏輯層和渲染層是分開的,小程序采用 AppService
和 WebView
的雙線程模型,基于 WebView
和原生控件混合渲染的方式,小程序的兩個線程沒有互斥的關(guān)系,js腳本線程和GUI渲染線程在 Native 客戶端的協(xié)調(diào)下可以有條不紊的同時執(zhí)行。
雙線程模型運行機制
小程序的渲染層和邏輯層分別由2個線程管理:渲染層的界面使用了WebView 進行渲染;邏輯層則使用Javascript引擎解析和執(zhí)行JS邏輯代碼。一個小程序存在多個頁面,所以渲染層存在多個 WebView 線程。
渲染層多線程的原因,便于維護多頁面的狀態(tài):
例如:一款新聞信息流小程序,它有兩個頁面,A頁面是新聞信息流,B頁面是新聞詳情頁。那么當用戶往下滑了很久后發(fā)現(xiàn)一個感興趣的新聞,點擊跳轉(zhuǎn)到詳情頁,看完之后想回到A頁面剛才的位置繼續(xù)瀏覽。單頁應(yīng)用由于把A頁面給擦掉了,所以這種場景下當從B回到A時,會發(fā)現(xiàn)A又重新刷新了一遍,體驗非常糟糕。
TIPS:
官方說渲染層和邏輯層分別是兩個線程,個人覺得容易引起歧義,更嚴謹一點說是兩個進程中的兩個線程,即WebView
進程和App
進程,官方文檔中有這樣一句話:
當小程序基于 WebView 環(huán)境下時,WebView 的 JS 邏輯、DOM 樹創(chuàng)建、CSS 解析、樣式計算、Layout、Paint (Composite) 都發(fā)生在同一線程
,在 WebView 上執(zhí)行過多的 JS 邏輯可能阻塞渲染,導致界面卡頓。以此為前提,小程序同時考慮了性能與安全,采用了目前稱為「雙線程模型」的架構(gòu)。
上面說的 “同一線程” 其實指的是webView
的渲染進程(渲染進程中包含渲染線程和JS主線程等),網(wǎng)頁開發(fā)中提到的渲染線程與JS主線程互斥和阻塞的問題在 WebView
環(huán)境下也不例外,所以小程序則將 JS 邏輯部分拆到App級(App進程中不同于WebView環(huán)境,并沒有window
document
等對象)的JS線程中運行,即所謂的AppService
,WebView
與App
是獨立的運行環(huán)境,雖然不會發(fā)生阻塞,但是也無法共享數(shù)據(jù),所以要依靠 Native
來通訊。
所以理論上是可以在wxs中訪問到window對象的,因為wxs代碼運行在 webview 中,但是微信對wxs功能做了閹割限制,尤其不能讓用戶操作DOM。
通訊過程如下:
兩個線程的通信是基于微信客戶端提供的WeixinJsBridge來實現(xiàn)的,像一個橋梁一樣將小程序的運行環(huán)境和微信客戶端(native連接了起來),同時也負責在渲染層和邏輯層之間傳遞數(shù)據(jù)和事件的工作。
數(shù)據(jù)傳輸通過邏輯層和視圖層兩邊提供的evaluateJavascript
實現(xiàn)的,用戶傳輸?shù)臄?shù)據(jù),需要將其轉(zhuǎn)換為字符串形式傳遞,同時把轉(zhuǎn)換后的數(shù)據(jù)內(nèi)容拼接成一份 JS 腳本,再通過執(zhí)行 JS 腳本的形式傳遞到兩邊的獨立環(huán)境。
請求也是通過WeixinJsBridge由微信客戶端代為發(fā)起。
以上這樣的運行機制,優(yōu)缺點都很明顯:
雙線程模型的優(yōu)缺點
優(yōu)點:
將邏輯層和渲染層隔離開,用戶無法直接操作DOM,提供了相對封閉和安全的運行環(huán)境。
JS不會阻塞渲染,但是大部分情況下視覺都要依賴JS中處理的數(shù)據(jù),JS阻塞時也不會通知視圖去更新,所以這條優(yōu)點其實意義不是很大
所有的頁面和組件的邏輯都在一個線程(AppService
)里,使用同一個上下文環(huán)境,比較好做狀態(tài)共享或跨頁面通訊。
缺點:
缺點在于每一次數(shù)據(jù)傳遞都要進行一次線程之間的通信,業(yè)務(wù)邏輯跟渲染層天然隔離,造成通信開銷大、延遲高等問題,通信越頻繁、數(shù)據(jù)量越大,則性能瓶頸越嚴重。
每個頁面都創(chuàng)建一個WebView
線程處理,有更多的內(nèi)存、時間開銷。
渲染層和邏輯層狀態(tài)要維護兩份,進一步加重內(nèi)存、時間開銷,并且沒有辦法完全保證兩份數(shù)據(jù)狀態(tài)實時保持一致,例如僅使用 this.data
更新數(shù)據(jù)而不是通過setData
時,那么實際渲染的值與邏輯層的值就不一致,某些場景下會造成非預期的問題。
尤其當 this.data.obj 這個obj指向一個引用地址的時候,帶來的問題更加不可預期。
this.viewModal = {obj: {a: 3}};
this.setData({obj: this.viewModal.obj}); // 此時邏輯層的data.obj已經(jīng)指向viewModal.obj對象的地址
this.viewModal.obj.a = 666;
this.data.obj.a === 666; // true
// 之后無論你在任何地方不小心更改了this.viewModal.obj,那么this.data.obj也變了,而此時在渲染層的數(shù)據(jù)還是舊數(shù)據(jù),你本意是想通過this.data獲取當前被渲染的值,但是這時候可能值已經(jīng)并不符合你的預期了。
小程序并非所有組件都是通過 webview
來渲染的。比如像 video, canvas, map 等稱為 native compoennt 的組件是直接用客戶端的原生組件渲染的,這意味著這些組件的性能更好。
總體上從開發(fā)者的角度來看,我認為這個架構(gòu)并不是一個很好的架構(gòu),邏輯層與渲染層隔離,帶來的問題遠遠比它解決的問題更多。
一個應(yīng)用里面的多個頁面,你認為是共享的狀態(tài)多,還是獨立的狀態(tài)多?用戶在操作一個頁面時,更關(guān)心跨頁面通信實時性,還是更關(guān)心當前頁面的渲染實時性?
我們當然更關(guān)心性能,但是從微信的角度來說,他們想既能享受 web
生態(tài)的好處的同時也能限制 web
的開放性,增強自己對平臺內(nèi)容的管控程度,從禁用eval
和new Function()
上就能看出一二。
當然微信也知道架構(gòu)所帶來的性能問題,所以發(fā)明了 WXS
,讓一部分 js
代碼能在渲染層跑,部分解決通信消耗和延遲的問題,還提供了一系列的性能優(yōu)化指南。
而近期,又有重磅更新放出,新渲染引擎 Skyline
橫空出世。
Skyline 新渲染引擎
官方聲稱新引擎能夠解決我們上面的吐槽:
在 Skyline 環(huán)境下,我們嘗試改變這一情況:Skyline 創(chuàng)建了一條渲染線程來負責 Layout, Composite 和 Paint 等渲染任務(wù),并在 AppService 中劃出一個獨立的上下文,來運行之前 WebView 承擔的 JS 邏輯、DOM 樹創(chuàng)建等邏輯。這種新的架構(gòu)相比原有的 WebView 架構(gòu),有以下特點:
界面更不容易被邏輯阻塞,進一步減少卡頓
無需為每個頁面新建一個 JS 引擎實例(WebView),減少了內(nèi)存、時間開銷
框架可以在頁面之間共享更多的資源,進一步減少運行時內(nèi)存、時間開銷
框架的代碼之間無需再通過 JSBridge 進行數(shù)據(jù)交換,減少了大量通信時間開銷
而與此同時,這個新的架構(gòu)能很好地保持和原有架構(gòu)的兼容性,基于 WebView 環(huán)境的小程序代碼基本上無需任何改動即可直接在新的架構(gòu)下運行。
牛蛙~ 牛蛙~,由于我也是剛得知消息,新引擎體驗報告待我后續(xù)奉上🐶。
其實本篇文章就是看到了新渲染引擎的消息后想寫的,各位先當做個鋪墊,有助于我們更好地去理解和使用新引擎。
最后,說了半天Webview
,那 Webview
到底是個啥,這里再給大家解釋一下。
WebView
WebView是術(shù)語,是指網(wǎng)頁視圖。可以內(nèi)嵌在移動端,實現(xiàn)前端的混合式開發(fā),大多數(shù)混合式開發(fā)框架都是基于WebView模式進行二次開發(fā)的。比如:APIcloud、uni-app等等的框架。
WebView
是一個基于webkit
引擎、展現(xiàn)web頁面的控件,用于在手機系統(tǒng)中展示網(wǎng)頁,可以簡單的理解為一個可以嵌套到界面上的一個瀏覽器控件。
webkit
是一個瀏覽器引擎,也就是瀏覽器內(nèi)核,處理CSS、DOM、渲染等。
不同運行環(huán)境中,使用的WebView
不同。
原文鏈接
該文章在 2023/7/29 10:05:52 編輯過