欧美三级国产三级日韩三级_亚洲熟妇丰满大屁股熟妇_欧美亚洲成人一区二区三区_国产精品久久久久久模特

Rax 小程序運(yùn)行時(shí)方案解密與思考 - 新聞資訊 - 云南小程序開(kāi)發(fā)|云南軟件開(kāi)發(fā)|云南網(wǎng)站建設(shè)-昆明葵宇信息科技有限公司

159-8711-8523

云南網(wǎng)建設(shè)/小程序開(kāi)發(fā)/軟件開(kāi)發(fā)

知識(shí)

不管是網(wǎng)站,軟件還是小程序,都要直接或間接能為您產(chǎn)生價(jià)值,我們?cè)谧非笃湟曈X(jué)表現(xiàn)的同時(shí),更側(cè)重于功能的便捷,營(yíng)銷(xiāo)的便利,運(yùn)營(yíng)的高效,讓網(wǎng)站成為營(yíng)銷(xiāo)工具,讓軟件能切實(shí)提升企業(yè)內(nèi)部管理水平和效率。優(yōu)秀的程序?yàn)楹笃谏?jí)提供便捷的支持!

您當(dāng)前位置>首頁(yè) » 新聞資訊 » 小程序相關(guān) >

Rax 小程序運(yùn)行時(shí)方案解密與思考

發(fā)表時(shí)間:2021-1-5

發(fā)布人:葵宇科技

瀏覽次數(shù):81

2020 年 3 月,暨支持編譯時(shí)方案之后,Rax 小程序發(fā)布了支持運(yùn)行時(shí)方案的版本。截至目前,Rax 仍是業(yè)界唯一一個(gè)同時(shí)支持編譯時(shí)和運(yùn)行時(shí)方案的小程序開(kāi)發(fā)框架。本文將向大家介紹 Rax 小程序運(yùn)行時(shí)方案的原理以及我們的思考。


回顧編譯時(shí)方案


介紹運(yùn)行時(shí)方案之前,我們?cè)倩仡櫹率裁词蔷幾g時(shí)方案。顧名思義,編譯時(shí)方案?jìng)?cè)重于編譯,這其中的代表框架是 Taro v2.x。其通過(guò)靜態(tài)編譯的方式,將 JSX 轉(zhuǎn)換為小程序的模板語(yǔ)言(即 WXML/AXML 等),再輔以輕量級(jí)的運(yùn)行時(shí) JS 代碼,抹平小程序生命周期和 React 生命周期的差異,使用戶(hù)能夠以熟悉的 React DSL 進(jìn)行小程序開(kāi)發(fā)。Rax 的編譯時(shí)方案原理與 Taro v2.x 類(lèi)似,關(guān)于實(shí)現(xiàn)細(xì)節(jié),可以參考之前的文章 Rax 轉(zhuǎn)小程序鏈路原理解析(一) Rax 小程序編譯時(shí)方案原理解析 。區(qū)別于編譯時(shí)方案,運(yùn)行時(shí)方案?jìng)?cè)重在運(yùn)行時(shí)實(shí)現(xiàn)渲染能力,不依賴(lài)靜態(tài)編譯,因此幾乎沒(méi)有語(yǔ)法限制,這也是其最大的特點(diǎn)。下面就來(lái)看一下運(yùn)行時(shí)方案實(shí)現(xiàn)的原理。


誕生基礎(chǔ)


小程序的底層實(shí)現(xiàn)實(shí)際上也是基于 Web 技術(shù),但是反映至開(kāi)發(fā)者層面,與 Web 卻又大相徑庭。在小程序中,邏輯層和視圖層隔離,邏輯層通過(guò)唯一的 setData 方法將數(shù)據(jù)傳遞至視圖層觸發(fā)渲染,視圖層則通過(guò)事件的方式觸發(fā)邏輯層代碼,其架構(gòu)如下圖所示。相比 Web 開(kāi)發(fā)時(shí)開(kāi)發(fā)者可以通過(guò) JS 調(diào)用瀏覽器提供的 DOM/BOM API 隨心所欲操作渲染內(nèi)容,小程序的架構(gòu)更加封閉也更安全,但也意味著 Web 代碼無(wú)法直接在小程序上運(yùn)行。

對(duì)于現(xiàn)代的前端框架(React/Vue)來(lái)說(shuō),底層基本都是通過(guò)調(diào)用 DOM API 來(lái)創(chuàng)建視圖。而小程序的視圖層模板是需要開(kāi)發(fā)者事先寫(xiě)好的,這意味著動(dòng)態(tài)創(chuàng)建 DOM 的方式在小程序中不被允許。但是,小程序的自定義組件具有的『自引用』特性為動(dòng)態(tài)創(chuàng)建 DOM 打開(kāi)了突破口。所謂自引用,就是自定義組件支持使用自己作為子節(jié)點(diǎn),也就意味著通過(guò)遞歸引用的方式,我們能夠構(gòu)造任意層級(jí)和數(shù)量的 DOM 樹(shù)。

舉例來(lái)說(shuō),假設(shè)一個(gè)小程序自定義組件 element 的 WXML 模板如下所示:

<view
wx:if="{{r.tagName === 'view'}}"
id="{{r.nodeId}}"
>
<block
wx:for={{r.children}}”
wx:key="nodeId"
>
<element data="{{r: item}}" />
block>
view>
<text
wx:elif="{{r.tagName === 'text'}}"
>
{{r.content}}
text>

注意到,element 在模板中遞歸引用了自身,并通過(guò)條件判斷終止遞歸。那么,當(dāng)邏輯層通過(guò) setData 傳遞了以下一份數(shù)據(jù)過(guò)來(lái)時(shí):

{
"nodeId": "1",
"tagName": "view",
"children": [
{
"nodeId": "2",
"tagName": "text",
“content”: “我是?"
},
{
"nodeId": "3",
“tagName": "text",
"content": "rax"
}
]
}

最終呈現(xiàn)出來(lái)的視圖便成了:

<view>
<text>我是text>
<text>raxtext>
view>

通過(guò)這種方式,我們巧妙地實(shí)現(xiàn)了在 WXML 模板固定的情況下,根據(jù)傳入的 setData 數(shù)據(jù)來(lái)動(dòng)態(tài)渲染視圖的能力。而這,也正是運(yùn)行時(shí)方案能夠誕生的基礎(chǔ)。


基本原理


Rax 的運(yùn)行時(shí)方案脫胎自 kbone ——微信官方推出的小程序與 web 端同構(gòu)解決方案。kbone 的設(shè)計(jì)原理可以參考其官網(wǎng) 介紹 ,簡(jiǎn)單總結(jié)就是通過(guò)在邏輯層模擬 DOM/BOM API,將這些創(chuàng)建視圖的方法轉(zhuǎn)換為維護(hù)一棵 VDOM 樹(shù),再將其轉(zhuǎn)換成對(duì)應(yīng) setData 的數(shù)據(jù),最后通過(guò)預(yù)置好的模板遞歸渲染出實(shí)際視圖。從 DOM API 到維護(hù) VDOM 樹(shù)的過(guò)程基本原理并不復(fù)雜,createElement/appendChild/insertBefore/removeChild 等對(duì)應(yīng)著基本的數(shù)據(jù)結(jié)構(gòu)的操作。

熟悉 Rax 的同學(xué)應(yīng)該知道,為了支持跨端,Rax 有 driver 的設(shè)計(jì)。實(shí)際上,我們完全可以針對(duì)小程序端再編寫(xiě)一個(gè) driver,根據(jù)上述原理實(shí)現(xiàn)其接口 API 即可。但我們最后的選擇還是通過(guò)更底層的模擬 BOM/DOM API 來(lái)完成了整個(gè)渲染機(jī)制。這么做的考量是,第一,基于 kbone 開(kāi)發(fā),這是最快的一套方案,小程序端的 driver 只需復(fù)用 web 端的 driver-dom 即可,畢竟底層的 document window 變量都已經(jīng)模擬好;第二,則是因?yàn)槲覀兿霝殚_(kāi)發(fā)者提供更貼近 web 的開(kāi)發(fā)體驗(yàn)。這套方案意味著開(kāi)發(fā)者除了使用 JSX 之外,也是支持直接使用 BOM/DOM API 創(chuàng)建視圖的,靈活度會(huì)更高一點(diǎn)。我們把目光拉長(zhǎng)到整個(gè)市面上的小程序運(yùn)行時(shí)框架,remax 通過(guò) react-reconciler 直接從 VDOM 層和小程序?qū)樱?lèi)似上面說(shuō)的 Rax 小程序 driver 設(shè)計(jì)),而 kbone 和 Taro 3.0 都選擇通過(guò)模擬 Web 環(huán)境來(lái)實(shí)現(xiàn)渲染。這也與框架開(kāi)發(fā)人員的設(shè)計(jì)意圖有關(guān),見(jiàn)仁見(jiàn)智。Rax 小程序運(yùn)行時(shí)方案的基本原理圖如下所示:


事件系統(tǒng)


Rax 小程序運(yùn)行時(shí)中,模擬 DOM/BOM API 的庫(kù)為 miniapp-render,其支持的 API 如下:

除了處理渲染數(shù)據(jù)外,另一個(gè)比較重要的事情便是事件系統(tǒng)。其通過(guò) EventTarget 基類(lèi)實(shí)現(xiàn)了一套完整的事件派發(fā)機(jī)制。邏輯層 DOM 節(jié)點(diǎn)均繼承自 EventTarget ,通過(guò)唯一的 nodeId 來(lái)收集自身綁定事件。視圖層模板上的每個(gè)內(nèi)置組件都會(huì)綁定 nodeId ,并監(jiān)聽(tīng)所有可觸發(fā)的事件,比如一個(gè)簡(jiǎn)單的 view 標(biāo)簽,會(huì)將 bindtap/bindtouchstart/bindtouchend 等事件都進(jìn)行綁定。在事件觸發(fā)時(shí),通過(guò) event.currentTarget.dataset.nodeId 獲取到目標(biāo)節(jié)點(diǎn) id,再觸發(fā)該節(jié)點(diǎn)上用戶(hù)綁定的對(duì)應(yīng)函數(shù)。


工程設(shè)計(jì)


Rax 小程序運(yùn)行時(shí)的工程主體流程 follow 了 Rax Web 的設(shè)計(jì),Web 端 Webpack 打包出的 JS Bundle 可以在小程序運(yùn)行時(shí)中復(fù)用。我們通過(guò)插件將 miniapp-render 模擬出的 window 和 document 變量注入該 bundle,再生成一個(gè)固定的小程序項(xiàng)目骨架,在 app.js 中加載 JS Bundle 即可。其整體工程結(jié)構(gòu)如下圖所示:


MPA or SPA ?

以上架構(gòu)是逐步演進(jìn)的結(jié)果。最初,我們使用了 webpack 的多 entry 模式打包運(yùn)行時(shí)小程序代碼,也就是每個(gè)頁(yè)面都會(huì)作為一個(gè) entry 獨(dú)立打包。這使得從行為上來(lái)說(shuō)小程序更像一個(gè) MPA。這帶來(lái)的問(wèn)題就是頁(yè)面間公共依賴(lài)的代碼不在同一內(nèi)存中執(zhí)行,與原生小程序表現(xiàn)不符。這個(gè)差異導(dǎo)致我們最終決定變更工程打包模式。目前版本的 Rax 運(yùn)行時(shí)小程序更符合 SPA 的形式,所有的業(yè)務(wù)代碼都打包到了一個(gè) JS 文件中。

我們將 Rax 工程入口的 rax-app 包在小程序運(yùn)行時(shí)上的鏈路做了一定改造,其在初始化時(shí)會(huì)根據(jù)路由返回各個(gè)頁(yè)面的 render 函數(shù),該 render 函數(shù)創(chuàng)建 root 節(jié)點(diǎn)( document.createElement )將對(duì)應(yīng)的 Rax 組件掛載至 其上,并將 root 節(jié)點(diǎn) append 到 body 節(jié)點(diǎn)( document.body.appendChild )。小程序每個(gè)頁(yè)面在 onLoad 生命周期中,會(huì)創(chuàng)建一個(gè)獨(dú)立的 document 并設(shè)置為全局變量,然后調(diào)用其對(duì)應(yīng)的 render 函數(shù)進(jìn)行各個(gè)頁(yè)面的獨(dú)立渲染。


性能調(diào)優(yōu)


從上面的小程序運(yùn)行時(shí)原理來(lái)看,其性能相比原生是存在一定差距的,這主要由以下幾個(gè)方面造成:第一:邏輯層運(yùn)行完整的 Rax + 通過(guò)模擬 DOM/BOM API 處理 VDOM 并生成 setData 數(shù)據(jù),需要消耗更多的計(jì)算時(shí)間;第二,相比原生小程序需要傳遞更多 setData 數(shù)據(jù),如果容器層數(shù)據(jù)序列化能力較弱,會(huì)大大增加數(shù)據(jù)傳輸耗時(shí);第三,視圖層通過(guò)自定義組件遞歸動(dòng)態(tài)生成視圖,而我們知道遞歸動(dòng)作本身就是一個(gè)性能損耗點(diǎn)。此外,由于無(wú)法預(yù)先知曉用戶(hù)需要綁定的屬性和事件,自定義組件模板中只能將所有屬性和事件預(yù)先綁好,這導(dǎo)致小程序運(yùn)行過(guò)程中會(huì)觸發(fā)很多無(wú)用的事件,進(jìn)一步加重負(fù)擔(dān)。經(jīng)過(guò)我們的 benchmark 計(jì)算,在支付寶小程序平臺(tái)上,運(yùn)行時(shí)小程序框架(包括 Rax/Taro/Remax 等)與原生小程序存在約 40% 的性能差距。

Rax 小程序運(yùn)行時(shí)發(fā)布后,經(jīng)測(cè)試其性能相比其他運(yùn)行時(shí)框架存在著較為明顯的差距,于是我們啟動(dòng)了性能調(diào)優(yōu)的專(zhuān)項(xiàng)計(jì)劃。通過(guò)以下方面的重構(gòu),成功將 Rax 小程序運(yùn)行時(shí)小程序的性能拉升至業(yè)界領(lǐng)先水平,與 Taro/Remax 基本處于同一水平線。

  • 更新數(shù)據(jù)精確化。在舊版本中,setData 的數(shù)據(jù)是全量更新的,雖然有 dom 子樹(shù)分割批量更新的設(shè)計(jì),但是數(shù)據(jù)傳輸仍然存在大量冗余。重構(gòu)版本中,Rax 增加了節(jié)點(diǎn)渲染判斷,未掛載節(jié)點(diǎn)無(wú)須觸發(fā)更新;將所有更新收攏至頂層 root 節(jié)點(diǎn)統(tǒng)一批量處理, 并且通過(guò)精確計(jì)算數(shù)據(jù)更新的 path,實(shí)現(xiàn)局部更新。比如某次更新節(jié)點(diǎn)的 class 屬性時(shí),setData 的數(shù)據(jù)可能是:

{
"root.children.[0].children.[1].class": "active"
}
  • 內(nèi)置小程序組件無(wú)需維護(hù)其屬性列表,而是根據(jù)用戶(hù)傳參直接賦值。舊版本中,我們維護(hù)了所有內(nèi)置組件的屬性,在獲取屬性值的時(shí)候均需要調(diào)用 domNode.getAttribute,具有一定性能開(kāi)銷(xiāo)。重構(gòu)版本 Rax 直接根據(jù)用戶(hù)傳參給屬性賦值,并將默認(rèn)值設(shè)置的操作移至視圖層 WXS/SJS 中處理。

  • 更新 miniapp-render 中的數(shù)據(jù)結(jié)構(gòu)。經(jīng)過(guò)梳理,Rax 移除了冗余的 tree 數(shù)據(jù),重寫(xiě)了 getaElementById 等 API;重構(gòu)了 attribute、classList 等類(lèi);使用了更符合場(chǎng)景需要的 Map/Set 等數(shù)據(jù)結(jié)構(gòu),提升了整體的數(shù)據(jù)處理性能。

  • 渲染模板優(yōu)化。在支付寶小程序中,Rax 使用 template 進(jìn)行遞歸調(diào)用;在微信中,Rax 使用 template 調(diào)用 element 再調(diào)用 template 的形式以避免微信端遞歸調(diào)用 template 的層數(shù)限制。在模板中,我們盡量使用 template is 語(yǔ)法進(jìn)行判斷,減少 a:if/wx:if 條件判斷,提升模板遞歸時(shí)的性能。


混合使用


無(wú)論是出于舊有業(yè)務(wù)的遷移,或者是出于性能考慮,Rax 小程序運(yùn)行時(shí)中都存在著混合使用的需求。目前,Rax 已經(jīng)打通與小程序內(nèi)置組件、小程序自定義組件、小程序頁(yè)面、小程序插件混合使用的能力。這其中,使用小程序自定義組件是最為復(fù)雜的。


與小程序自定義組件混用

在 Rax 中使用小程序自定義組件,其引入路徑需要與 usingComponents 保持一致(例如 import CustomComp from '../components/CustomComp/index' )。在編譯階段,Rax 工程使用 Babel 插件進(jìn)行代碼掃描,檢測(cè)到 JSX 中使用的某個(gè)組件是小程序自定義組件(根據(jù)其引入路徑是否存在同名 axml 文件)時(shí),會(huì)將其使用到的屬性和事件進(jìn)行緩存,然后通過(guò) webpack 插件動(dòng)態(tài)生成至遞歸模板中。在運(yùn)行時(shí)中的創(chuàng)建節(jié)點(diǎn)階段,通過(guò)查詢(xún)緩存判斷節(jié)點(diǎn)是否為自定義組件。若是自定義組件,則其渲染數(shù)據(jù)中會(huì)插入緩存中的屬性,并且綁定事件至該自定義組件實(shí)例。


與編譯時(shí)組件混用(雙引擎混合)

通過(guò) Rax 小程序編譯時(shí)方案產(chǎn)出的組件,從使用形態(tài)上來(lái)說(shuō),可以直接視為小程序自定義組件。而 Rax 工程加強(qiáng)了運(yùn)行時(shí)與編譯時(shí)的聯(lián)系,當(dāng)在 Rax 小程序運(yùn)行時(shí)中使用編譯時(shí)組件 npm 包時(shí),用戶(hù)無(wú)需引入組件的具體路徑,只需像使用普通組件時(shí)一樣引入,Rax 工程將自動(dòng)根據(jù)該組件 package.json 中是否存在 miniappConfig 字段來(lái)判斷其是否為一個(gè) Rax 多端組件,然后直接使用其編譯時(shí)的組件實(shí)現(xiàn)。


未來(lái)優(yōu)化方向


Rax 作為業(yè)界唯一一個(gè)同時(shí)支持編譯時(shí)和運(yùn)行時(shí)引擎的小程序開(kāi)發(fā)方案,其雙引擎混合使用的能力能夠較為完美地實(shí)現(xiàn)性能與開(kāi)發(fā)效率的平衡。在未來(lái),Rax 將實(shí)現(xiàn)更為靈活的雙引擎混合使用方式,如支持在單個(gè)項(xiàng)目中指定某個(gè)組件以編譯時(shí)引擎編譯,為業(yè)務(wù)提供更高的靈活度。


總結(jié)


以上就是 Rax 小程序運(yùn)行時(shí)方案的原理解析。運(yùn)行時(shí)方案解決了編譯時(shí)方案天生的語(yǔ)法限制問(wèn)題,但是也存在著明顯的性能掣肘??梢哉f(shuō),在當(dāng)前 2020 年這個(gè)節(jié)點(diǎn),小程序開(kāi)發(fā)仍然沒(méi)有所謂的銀彈,也許 Rax 小程序雙引擎的融合會(huì)是一個(gè)相對(duì)范圍內(nèi)的最優(yōu)解。逆標(biāo)準(zhǔn)而上的小程序究竟能走多遠(yuǎn),誰(shuí)也無(wú)法下定論,未來(lái)相當(dāng)一段時(shí)間內(nèi),開(kāi)發(fā)者仍然要面臨種種問(wèn)題。站在小程序開(kāi)發(fā)框架的角度,只希望所有開(kāi)發(fā)者能夠選擇對(duì)自己最合適的框架,爽快而又高效地完成小程序的開(kāi)發(fā)。

相關(guān)案例查看更多

相關(guān)閱讀