国产成人精品无码青草_亚洲国产美女精品久久久久∴_欧美人与鲁交大毛片免费_国产果冻豆传媒麻婆精东

所在位置: 首頁(yè) > 營(yíng)銷(xiāo)資訊 > 網(wǎng)站運(yùn)營(yíng) > 北海(Kraken)構(gòu)建大前端混合渲染技術(shù)體系 —— Web 與 Flutter Widget 混合渲染方

北海(Kraken)構(gòu)建大前端混合渲染技術(shù)體系 —— Web 與 Flutter Widget 混合渲染方

時(shí)間:2023-06-03 06:15:01 | 來(lái)源:網(wǎng)站運(yùn)營(yíng)

時(shí)間:2023-06-03 06:15:01 來(lái)源:網(wǎng)站運(yùn)營(yíng)

北海(Kraken)構(gòu)建大前端混合渲染技術(shù)體系 —— Web 與 Flutter Widget 混合渲染方案:

背景

組件(模塊)封裝與開(kāi)發(fā)可以給前端業(yè)務(wù)開(kāi)發(fā)的過(guò)程帶來(lái)非常大的研發(fā)效能的提升,各個(gè)業(yè)務(wù)域的開(kāi)發(fā)者會(huì)定制開(kāi)發(fā)許多符合自己業(yè)務(wù)場(chǎng)景的基礎(chǔ)組件(模塊)沉淀一套快速?gòu)?fù)用的物料體系,以保證業(yè)務(wù)開(kāi)發(fā)的研發(fā)效能。同樣,在各個(gè) Flutter 團(tuán)隊(duì),也有大量的 Flutter Widget 的物料,以及各種基于 Flutter 場(chǎng)景做的性能優(yōu)化。在大前端的視角下,我們期望在端內(nèi)擁有 Web 開(kāi)發(fā)的研發(fā)效能以及動(dòng)態(tài)性的同時(shí),也期望通過(guò)一些 Native 的優(yōu)化手段讓?xiě)?yīng)用擁有媲美原生的體驗(yàn)與性能。

北海(Kraken) 作為一款高性能、易擴(kuò)展的 Web 標(biāo)準(zhǔn)渲染引擎,通過(guò)上層實(shí)現(xiàn) W3C 標(biāo)準(zhǔn)以提供前端開(kāi)發(fā)者較低的學(xué)習(xí)成本以及快速?gòu)?fù)用已有的前端研發(fā)體系的能力。眾所周知,除了根據(jù)前端框架做一些業(yè)務(wù)組件開(kāi)發(fā),在前端有一套為容器(瀏覽器)標(biāo)準(zhǔn)的擴(kuò)展定制能力的技術(shù) —— Web Components。Web Component 更多地提供開(kāi)發(fā)者創(chuàng)建可重用的定制 element 的能力,而它本身基于 Web 技術(shù)棧還是存在一些限制,比如說(shuō)無(wú)法直接管理 Render Object 達(dá)到在長(zhǎng)列表下一個(gè)動(dòng)態(tài)回收能力等。作為一個(gè) UI 能力的復(fù)用以及對(duì)基礎(chǔ)原子組件能力的封裝,Web Components 是完全夠用的,但是對(duì)于在端內(nèi)在節(jié)點(diǎn)容器上提供一些默認(rèn)的 Native 級(jí)別的優(yōu)化,Web Components 顯得并沒(méi)有那么突出,這里面其實(shí)還可以有更大的想象空間。

那么,Kraken 本身是一款基于 Flutter 技術(shù)開(kāi)發(fā)的 Web 渲染引擎,是否可以復(fù)用 Flutter 生態(tài),將 Flutter Widget 能力融合進(jìn) Web 渲染能力中呢?這樣可以復(fù)用大量 Widget 的業(yè)務(wù)組件,采用同一條渲染管線(xiàn)高效渲染的同時(shí),也在端內(nèi)提供了更多客戶(hù)端(Widget)的性能優(yōu)化手段,以形成一套“動(dòng)態(tài)化”、“高性能”、“易擴(kuò)展”的融合 Web 與 Flutter 生態(tài)的大前端技術(shù)體系。

用 Flutter Widget 實(shí)現(xiàn)一個(gè) Custom Element

要把 Flutter 生態(tài)融合進(jìn) Kraken 中去,我們期望能讓開(kāi)發(fā)者把 Flutter Widget 作為一種 Custom Element 接入到 Kraken 體系中。

首先開(kāi)發(fā)者需要繼承 WidgetElement 實(shí)現(xiàn)一個(gè) FlutterContainerWidgetElement ,并通過(guò) defineCustomElement 注冊(cè)到 Kraken 中去,使其成為一個(gè) Custom Element。在 FlutterContainerWidgetElement 的 build 方法中,每當(dāng)該節(jié)點(diǎn)的 attribute 或者子節(jié)點(diǎn)變化(比如 appendChild),該節(jié)點(diǎn)就會(huì)被標(biāo)臟,依賴(lài) Flutter 的生命周期,該節(jié)點(diǎn)最終會(huì)重新被 build,Kraken 在此時(shí)會(huì)把該節(jié)點(diǎn)最新的 properties 以及 children (對(duì)應(yīng)到前端就是 setAttribute 以及 dom 節(jié)點(diǎn))傳遞過(guò)來(lái),Widget 根據(jù)這些參數(shù)完成一次 build,最終更新到界面上。

下面是一個(gè)將 Flutter 的 Column Widget 實(shí)現(xiàn)一個(gè) ColumnWidgetElement 以提供給前端一個(gè)列布局容器的 Demo。

void main() { // 定義 tagname 以及 注冊(cè) Custom Element。 Kraken.defineCustomElement('flutter-column', (context) { return ColumnWidgetElement(context); });}// 繼承 WidgetElement 實(shí)現(xiàn) Custom Element,在 build 方法中返回對(duì)應(yīng) Widiget。class ColumnWidgetElement extends WidgetElement { ColumnWidgetElement(EventTargetContext? context) : super(context); // Build 方法會(huì)默認(rèn)將前端設(shè)置給 Element 的 attribute(properties)以及該節(jié)點(diǎn)的 children(自動(dòng)轉(zhuǎn)換成一個(gè) Widget 的 List)傳遞過(guò)來(lái),這邊只需要返回一個(gè)開(kāi)發(fā)者自己實(shí)現(xiàn)的 Widget 即可。 @override Widget build(BuildContext context, Map<String, dynamic> properties, List<Widget> children) { return Column( // 可以通過(guò)前端設(shè)置的 attribute 來(lái)觸發(fā) Widget 的邏輯與配置。 textDirection: properties['direction'] ? TextDirection.ltr : TextDirection.ltr, // 前端 appendchild 的所有子節(jié)點(diǎn)會(huì)包裝成 Widget List,直接使用即可。 children: children, ); }}相應(yīng)的在 JavaScript 中,前端開(kāi)發(fā)者可以直接調(diào)用生成 flutter-column 即可調(diào)用該 Widget 能力。

const column = document.createElement('flutter-column');document.body.appendChild(column);for (let i = 0; i < 10; i++) { column.appendChild(document.createTextNode(i));}

技術(shù)原理

那么,要實(shí)現(xiàn)這樣一套混合渲染的方案的前提,得搞明白 Kraken 以及 Flutter Widget 的渲染流程。

注:Render Object Tree 中,K 代表 Kraken 生成的 Render Object,F(xiàn) 則代表 Flutter Widget 生成的 Render Object(下同)。

Kraken 渲染流程跟 Web 非常類(lèi)似,經(jīng)典的三棵樹(shù)——CSSOM Tree、DOM Tree 以及 Render Object Tree,與之相對(duì)應(yīng)的 Flutter 渲染流程也有經(jīng)典的三棵樹(shù)—— Widget Tree、Element Tree 以及 RenderObject Tree。當(dāng)最終生成 RenderObject Tree 以后,后續(xù)的合成光柵化上屏操作是一致的。對(duì)于 Kraken 技術(shù)原理不熟悉的小伙伴,可以通過(guò)筆者的另一篇文章——《深入解析基于 Flutter 的 Web 渲染引擎「北海 Kraken 」技術(shù)原理》 來(lái)了解 Kraken 的實(shí)現(xiàn)思路以及更多技術(shù)原理細(xì)節(jié)。

不難發(fā)現(xiàn),在上述的渲染流程中最終生成的 Render Object Tree 里 Kraken 生成的 Render Object 與 Flutter Widget 生成的 Render Object 是完全獨(dú)立的兩棵子樹(shù),無(wú)法混合成如下所述的互相嵌套:

我們知道,最終影響渲染的只是 Render Object Tree,而上層的 DOM(或 Flutter Element)在各自的生命周期以及 API 中承擔(dān)了不同的角色,它們提供的是對(duì)不同的開(kāi)發(fā)者生態(tài)(Web 生態(tài)以及 Flutter 生態(tài))的支持。如果強(qiáng)行把 Widget 或者 Flutter Element 侵入到 Web 標(biāo)準(zhǔn)的 DOM 中去,會(huì)導(dǎo)致各種差異難以抹平,長(zhǎng)久看是無(wú)法持續(xù)維護(hù)與迭代的。

所以我們期望通過(guò) Adapter 來(lái)適配兩套標(biāo)準(zhǔn),最終通過(guò)各自的生命周期的橋接來(lái)組裝出最終的 Render Object Tree,最終達(dá)到混合渲染的目標(biāo)。基于這個(gè)設(shè)想,我們可以得到如下的渲染流程:

可以看到,這里面核心就是需要處理四棵樹(shù)(DOM、Widget、Flutter Element 以及 Render Object),其中 CSS 是依賴(lài)與 DOM 上的,不需要做額外的處理。上圖中通過(guò) Adapter 完成 DOM 與 Widget 以及 Flutter Element 之間的互相綁定與轉(zhuǎn)換,最終通過(guò)各自生命周期的橋接將當(dāng)前節(jié)點(diǎn)產(chǎn)生的 Render Object(無(wú)論是 DOM 產(chǎn)生的還是 Flutter Element 產(chǎn)生的)直接掛在到父親節(jié)點(diǎn)的 Render Object 上,形成最終混合在一起的 Render Object Tree。

實(shí)際上這四棵樹(shù)就會(huì)產(chǎn)生如下的一個(gè)對(duì)應(yīng)關(guān)系:

乍一看四棵樹(shù)在不同情況下排列組合成了一堆非常復(fù)雜的樹(shù)形結(jié)構(gòu),實(shí)際上確實(shí)非常復(fù)雜。由于需要支持 Flutter Widget 作為一個(gè) Custom Element 存在 Web 的渲染管線(xiàn)中,那么無(wú)論是互相嵌套還是插入或者被插入到普通 DOM 節(jié)點(diǎn)上,都需要對(duì)應(yīng)的支持,排列組合一下就是下面這些情況:

上圖的四棵樹(shù)的互相對(duì)應(yīng)關(guān)系其實(shí)就包含了上述的幾種情況,我分別從頭開(kāi)始梳理一下。

Kraken 本身是一個(gè) Widget ,它是可以被嵌套在其他 Flutter Widget 的內(nèi)部的,所以在 Kraken 的頂層會(huì)有一個(gè)對(duì)應(yīng)的 rootFlutterElement ,在 Kraken Widget 的生命周期 mount 中,會(huì)將該 Flutter Element 的指針存儲(chǔ)起來(lái),以供后續(xù)使用(后面的 Flutter Element 需要掛載在 Kraken 的根節(jié)點(diǎn)上)。此外,初始化過(guò)程會(huì)創(chuàng)建出大家熟悉的 Window、Document、Body 以及 Head 等節(jié)點(diǎn),同時(shí)創(chuàng)建出對(duì)應(yīng)的 Render Object。

接著是中間一部分,這里展示了一個(gè) Flutter Widget 作為容器(下面繼續(xù)插入 DOM 子節(jié)點(diǎn))以及普通 DOM 在 Kraken 內(nèi)部的一個(gè)渲染流程。首先是普通的 DOM,以最右側(cè)黃色樹(shù)的 DIV Element 以及 TextNode 為例,它們插入到 BODY 節(jié)點(diǎn)上就是默認(rèn)的 WEB 渲染流程,會(huì)生成對(duì)應(yīng)的 RenderFlowLayout 以及 RenderTextBox、RenderParagraph 作為對(duì)應(yīng)的 Render Object 插入到 Render Object Tree 中。當(dāng)一個(gè) Flutter Widget (WidgetElement)作為節(jié)點(diǎn)插入到 DOM 樹(shù)中時(shí),會(huì)有一個(gè) WidgetElement(繼承自 dom.Element)作為 DOM 結(jié)構(gòu)插入到 DOM 樹(shù)中去。那么這個(gè)以 Flutter Widget 作為實(shí)現(xiàn)的 DOM 節(jié)點(diǎn),是如何驅(qū)動(dòng) Flutter Widget 產(chǎn)生 Flutter Element 以及對(duì)應(yīng)的 Render Object 并掛在到對(duì)應(yīng)的父節(jié)點(diǎn)的 Render Object 上的呢?

首先,WidgetElement 類(lèi)在初始化時(shí)會(huì)創(chuàng)建一個(gè) Stateful 的 Widget——_KrakenAdapterWidget,當(dāng)一個(gè) DOM 節(jié)點(diǎn)的 attribute 或者 通過(guò) appendChild 等操作插入(或刪除)一個(gè)子節(jié)點(diǎn)時(shí)候,會(huì)需要通過(guò)觸發(fā) build 將 Widget 標(biāo)臟,使該 Widget 可以通過(guò) Flutter 的生命周期重新構(gòu)建輸出。同時(shí),該 Adapter 也會(huì)對(duì)所有子節(jié)點(diǎn)進(jìn)行處理,如果子節(jié)點(diǎn)是一個(gè) Flutter Widget,則直接通過(guò) build 方法構(gòu)建出對(duì)應(yīng)的 Widget,如果子節(jié)點(diǎn)是一個(gè)普通的 DOM,則會(huì)通過(guò) KrakenElementToWidgetAdaptor 來(lái)包裝一下該節(jié)點(diǎn),已橋接對(duì)應(yīng)的生命周期以及 Render Object(下面會(huì)講到普通 DOM 如何轉(zhuǎn)換成一個(gè) Flutter Widget)。

我們知道, DOM 最終被被插入 DOM Tree 后會(huì)將產(chǎn)生的 Render Object 也插入到 Render Object Tree,如果 DOM 節(jié)點(diǎn)是一個(gè) WidgetElement(Flutter Widget),那么就需要使用 Flutter Widget 最終生成的 Render Object。所以 override 了 didAttachRendere 生命周期,內(nèi)部調(diào)用了 _attachWidget,通過(guò)它將 Flutter Widget 最終產(chǎn)生的 Render Obejct 給 attach 到 Render Object Tree 上。

最后來(lái)看看上文已經(jīng)提到過(guò)的,一個(gè)普通 DOM 元素是如何融入到 Flutter Element 與 Widget 的渲染流程中去的。以 DIV Element 為例,在上文提到的 convertNodeListToWidgetList 中,會(huì)將一個(gè)普通 DOM 元素轉(zhuǎn)化成 KrakenElementToWidgetAdaptor 這個(gè) Widget,該 Widget 的 createElement 會(huì)生成對(duì)應(yīng)的 Flutter Element, 所以會(huì)被 override 掉,并生成 KrakenElementToFlutterElementAdaptor 這個(gè) Flutter Element。同樣的,該 Widget 我們并不希望它會(huì)按照 Widget 流程產(chǎn)生一個(gè) Render Object,而是直接用上述的 DIV Element 產(chǎn)生的 Render Object, 所以會(huì)通過(guò) createRenderObject 方法來(lái)直接返回 DIV Element 產(chǎn)生的 Render Object。

同樣,KrakenElementToFlutterElementAdaptor 這個(gè) Flutter Element,會(huì)在 mount 以及 umount 生命周期中觸發(fā) createRenderer 等方法,用 Flutter 的生命周期鉤子去保證 Kraken DOM 的一些流程的調(diào)用或者資源的釋放。

基于以上原理,Kraken 實(shí)現(xiàn)了 Flutter Widget 作為一個(gè) Custom Element 嵌入到 Kraken 中的任何地方。以 Demo 的 JS 代碼為例渲染 已注冊(cè)到 Kraken 內(nèi)部的 Flutter Widget,F(xiàn)lutter Widget 無(wú)論作為容器還是一個(gè)子節(jié)點(diǎn),都可以呈現(xiàn)在 Kraken 中,用 JS 來(lái)動(dòng)態(tài)修改。

進(jìn)階版:更多復(fù)雜的場(chǎng)景

用 Flutter Widget 優(yōu)化瀑布流場(chǎng)景性能

筆者以業(yè)務(wù)中常見(jiàn)的一種形態(tài)——瀑布流場(chǎng)景為例,找了一個(gè)社區(qū)的瀑布流 Widget 組件——waterfall_flow 以及一個(gè)下拉觸底刷新的 Widget 組件—— flutter_easyrefresh,我們期望把它作為一個(gè)瀑布流容器集成到 Kraken 的渲染流程中,讓它能夠在 JavaScript 中被當(dāng)作一個(gè) Element 調(diào)用。同時(shí),也期望它內(nèi)部提供的一些動(dòng)態(tài) Render Object 的回收能力,保證在長(zhǎng)列表下有一個(gè)滾動(dòng)流暢以穩(wěn)定的及內(nèi)存的表現(xiàn),而這些只需要開(kāi)發(fā)這個(gè) Flutter Widget 的同學(xué)去實(shí)現(xiàn),前端同學(xué)對(duì)于這部分優(yōu)化能力是無(wú)開(kāi)發(fā)以及理解成本的,對(duì)于前端同學(xué)來(lái)說(shuō),就像使用一個(gè) npm 包一樣簡(jiǎn)單,并且能夠讓性能做到容器級(jí)別的優(yōu)化。

void main() { Kraken.defineCustomElement('flutter-container', (context) { return EasyRefreshWidgetElement(context); });}class EasyRefreshWidgetElement extends WidgetElement { EasyRefreshWidgetElement(EventTargetContext? context) : super(context, defaultStyle: { 'height': '100vh', 'display': 'block' }); @override Widget build(BuildContext context, Map<String, dynamic> properties, List<Widget> children) { return EasyRefresh( child: WaterfallFlow.builder( // 所有 Web 標(biāo)準(zhǔn)的節(jié)點(diǎn)可以傳入到 Widget 中。 itemCount: children.length, itemBuilder: (BuildContext context, int index) => children[index], padding: EdgeInsets.all(5.0), gridDelegate: SliverWaterfallFlowDelegateWithFixedCrossAxisCount( crossAxisCount: 2, crossAxisSpacing: 5.0, mainAxisSpacing: 5.0, ), ), // 通過(guò) dispatchEvent 可以像 element 節(jié)點(diǎn)拋出符合 Web 標(biāo)準(zhǔn)的事件,前端通過(guò) addEventListener 監(jiān)聽(tīng)。 onRefresh: () async => dispatchEvent(Event('refresh')), onLoad: () async => dispatchEvent(Event('load')), ); }}此時(shí),由于該 Custom Element 已經(jīng)被注冊(cè)到 Kraken 中去,在前端只需要通過(guò) web 標(biāo)準(zhǔn)的 createElement 即可創(chuàng)建該節(jié)點(diǎn)。

const flutterContainer = document.createElement('flutter-container');以及該節(jié)點(diǎn)可以通過(guò) addEventListener 來(lái)監(jiān)聽(tīng) Widget 組件拋出的 Custom Event。

flutterContainer.addEventListener('refresh', () => {});最終渲染出來(lái)的內(nèi)容如下,可以看到內(nèi)部子節(jié)點(diǎn)的 Web 節(jié)點(diǎn)可以直接使用瀑布流 Widget 的布局能力。同時(shí),由于該 Widget 自帶的動(dòng)態(tài) Render Object 回收能力,可以使得子節(jié)點(diǎn)在滾動(dòng)時(shí)候動(dòng)態(tài)回收,保證流暢地滾動(dòng),并且內(nèi)存不會(huì)有明顯的增量。而這一切對(duì)于前端開(kāi)發(fā)者是沒(méi)有任何額外的接入以及理解成本的,F(xiàn)lutter Widget 接入 Web 體系中會(huì)完全按照 Web 標(biāo)準(zhǔn)呈現(xiàn)給前端。同樣,對(duì)于 Flutter 開(kāi)發(fā)者,依舊可以控制熟悉的三棵樹(shù)——Widget、Flutter Element 以及 Render Object,以提供一些端上的增強(qiáng)能力。

https://www.zhihu.com/video/1462014088896933888

最后

Kraken 在 follow 了 W3C 標(biāo)準(zhǔn)的同時(shí),也將 Flutter 的渲染能力融合進(jìn)整個(gè)體系,讓 Flutter 生態(tài)與 Web 生態(tài)融合渲染,成為一個(gè)融合渲染的大前端技術(shù)體系,互相取之所長(zhǎng),補(bǔ)其所短。讓 Web 前端生態(tài)提供的表現(xiàn)力、動(dòng)態(tài)性以及 Web 生態(tài)使業(yè)務(wù)可以快速開(kāi)發(fā)迭代,滿(mǎn)足大部分變化的業(yè)務(wù)。同時(shí)也讓 Native 的同學(xué)可以提供給前端開(kāi)發(fā)者多樣化的 UI 渲染能力,以及將更多的性能優(yōu)化手段運(yùn)用到整個(gè)體系中,使優(yōu)化的上限更高,體驗(yàn)更好。

同時(shí),基于 Kraken **“易擴(kuò)展”**這個(gè)點(diǎn),開(kāi)發(fā)者可以用非常低的定制成本根據(jù)自己的業(yè)務(wù)域開(kāi)發(fā)一款深度定制的 Web 渲染引擎。復(fù)用已有的基建以及生態(tài),給端內(nèi)的體驗(yàn)帶來(lái)更多的體驗(yàn)升級(jí)以及可能性。

最后,Kraken 的所有代碼都已經(jīng)開(kāi)源,Kraken 提供了開(kāi)放的 TSC 機(jī)制期望所有開(kāi)發(fā)者可以平等地交流以及決策,使 Kraken 可以更好地發(fā)展,也歡迎更多的開(kāi)發(fā)者一起來(lái)共建 Kraken。

Kraken Github:https://github.com/openkraken/kraken

Kraken 官網(wǎng):https://openkraken.com/

關(guān)鍵詞:渲染,混合,體系,技術(shù),北海

74
73
25
news

版權(quán)所有? 億企邦 1997-2025 保留一切法律許可權(quán)利。

為了最佳展示效果,本站不支持IE9及以下版本的瀏覽器,建議您使用谷歌Chrome瀏覽器。 點(diǎn)擊下載Chrome瀏覽器
關(guān)閉