日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 >

从各大跨平台技术说开去,我们真的需要虚拟 DOM 吗?

發(fā)布時間:2025/7/25 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 从各大跨平台技术说开去,我们真的需要虚拟 DOM 吗? 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

前言

你有沒有留意到?優(yōu)秀的解決方案思想都是相通的:當(dāng)你研究 Flutter 渲染原理時會發(fā)現(xiàn) Flutter Rendering 層類似于 React 中的虛擬 DOM,當(dāng)你去看 Weex 工作原理時,誒,又發(fā)現(xiàn)了虛擬 DOM 的身影,更別提 VUE 響應(yīng)式視圖的核心也是虛擬 DOM 了。

那這個虛擬 DOM 有什么用?為什么這么多框架都應(yīng)用了它?本質(zhì)上帶來了什么優(yōu)勢?本文將結(jié)合前端和移動端來談?wù)劇?/p>

什么是 DOM?什么是虛擬 DOM?

DOM 就是文檔樹,與用戶界面控件樹對應(yīng),在 web 開發(fā)中通常指 HTML 對應(yīng)的渲染樹,但廣義的 DOM 也可以指 Android 中的 XML 布局對應(yīng)的控件樹,而 DOM 操作就是指直接操作渲染樹(或控件樹)。

虛擬 DOM 是一個用來表示真實的 DOM 結(jié)構(gòu)的數(shù)據(jù)結(jié)構(gòu)。

思考

想當(dāng)年學(xué)前端的時候,還是 jQuery 的時代,想賦值?改個樣式?取值?都是document.getElementById()咔咔一頓操作。這樣直接操作 DOM 會有什么問題?

直接操作 DOM 帶來的問題

1. model 和 view 耦合

最直觀的問題之一, 把用戶請求的表現(xiàn)邏輯和控制層要實現(xiàn)的業(yè)務(wù)邏輯兩者混合起來了,兩部分的依賴非常強。

2. 高頻操作引起性能損耗

寫個簡單Demo,我們看下效果。

為什么會有性能損耗?

原因可以歸結(jié)為 2 點:

1. 跨界交流損耗

把 DOM 和 ECMAScript 各自想象成一個島嶼,它們之間用收費橋梁連接。 ????????????????????????????????????????????????????????????????????????????????——《高性能JavaScript》

DOM 屬于渲染引擎,而 JS 又是屬于 JS 引擎,在瀏覽器內(nèi)核中他們彼此獨立。單獨來看,兩者都是很快的,但當(dāng)我們用 JS 去操作 DOM 時,引擎之間進(jìn)行了“跨界交流”。這個“跨界交流”的實現(xiàn)并不簡單,它依賴了橋接接口作為“橋梁”,如下圖:

既然是收費橋梁,過“橋”就要收費。我們每操作一次 DOM(不管是為了修改還是僅僅為了訪問其值),都要過一次“橋”。次數(shù)一多就會產(chǎn)生比較明顯的性能問題。

那移動端混合開發(fā)的情況呢?

就拿 RectNative 舉例,RectNative 是一套 UI 基于原生控件(非Web UI)業(yè)務(wù)邏輯基于 JS 的跨平臺技術(shù)解決方案,JS 中所寫控件標(biāo)簽不是真實控件,會在 Native 端解析為原生控件,如<Text>標(biāo)簽對應(yīng) Android 中的 TextView 控件。

在布局過程中 RN 需要在 JS 和 Native 之間通信,如果遇到滑動和拖動的情況,劣勢就很明顯了,這和在瀏覽器中要 JS 頻繁操作 DOM 所帶來的問題是相同的,都會帶來比較可觀的性能開銷。

2. DOM 修改引起重繪或重排

修改 DOM 屬性的代價更是昂貴,它會導(dǎo)致渲染引擎重新計算幾何變化(重排和重繪)。我們來看下渲染步驟:

在頁面生成時,至少會進(jìn)行一次布局和渲染,后面用戶操作時,如果修改了 DOM 節(jié)點,會觸發(fā)渲染樹(Render Tree)的變化,從而進(jìn)行上圖的步驟2、3、4、5,因此如果在 js 中存在很多 DOM 操作,就會不斷地觸發(fā)重繪或重排,影響頁面性能。

在移動端,情況也好不到哪里去。

布局中的任何一個 View 一旦發(fā)生屬性變化,都可能引起很大的連鎖反應(yīng)(如果所在的控件層級非常復(fù)雜的話)。例如某個 btn 的大小突然增加一倍,有可能會導(dǎo)致兄弟視圖的位置變化,也有可能導(dǎo)致父視圖的大小發(fā)生改變。當(dāng)大量的 layout() 操作被頻繁調(diào)用執(zhí)行時,會引起整個 View 頻繁地重渲,最終導(dǎo)致丟幀或 UI 卡頓。

解決辦法

針對以上的問題,我們一一提出解決方案:

1. 減少跨界過橋次數(shù),合并操作

ECMAScript 每次訪問 DOM,都要經(jīng)過這座橋,并交納“過橋費”,訪問 DOM 的次數(shù)越多,費用也就越高。因此,推薦的做法是盡量減少過橋的次數(shù),努力呆在 ECMAScript 島上。???????????????????????????????????????????????????——《高性能JavaScript》

我們來分析下,怎么減少“過橋的次數(shù)”?過橋次數(shù)之所以頻繁,和頻繁的 DOM 操作有關(guān)。

比如我們給列表加數(shù)據(jù),最差的方式就是這樣:

for (var i = 0; i < N; i++) {var li = document.createElement("li");li.innerHTML = arr[i];ul.appendChild(li);} 復(fù)制代碼

這里會操作 N 次 DOM 觸發(fā) N 次重繪。重渲肯定是無法避免的,我們的目標(biāo)是最小化重繪和重排次數(shù)。

那能不能不要立即去操作 DOM 呢?

將這 N 次更新的內(nèi)容保存到一個 js 對象中,最終將這個 js 對象一次性 attach 到 DOM 樹上,通知瀏覽器去執(zhí)行繪制工作。這樣無論多么復(fù)雜的 DOM 操作,最終都只會觸發(fā)一次渲染全流程,避免了大量的無謂計算量,這樣不就可以了么!(欣喜若狂.jpg)

但優(yōu)化 DOM 操作方式很多,不一定要依賴虛擬 DOM,所以這不是我們需要虛擬 DOM 的根本原因,根本的原因還是響應(yīng)式需求。

2. 響應(yīng)式

如果通過 JS 直接操作 DOM 的話,勢必會造成視圖數(shù)據(jù)和模型數(shù)據(jù)的不匹配,我們能不能讓開發(fā)者只關(guān)心狀態(tài)(數(shù)據(jù))變化,而無需關(guān)心控件操作呢?當(dāng)然可以!

React 中提出一個重要思想:狀態(tài)改變則 UI 隨之自動改變。

每次狀態(tài)有變動就重構(gòu)用戶界面,重渲整個 view。如果沒有虛擬 DOM,簡單粗暴的做法就是直接重置 innerHTML,在大部分?jǐn)?shù)據(jù)都變了的情況下,重置 innerHTML 還算合理,但如果只有一行數(shù)據(jù)變了,顯然就有大量的浪費。

這是我們需要虛擬 DOM 的原因,用它來代替開發(fā)者的手工操作,確保只對真正有變化的部分進(jìn)行實際的 DOM 操作(局部刷新)。

3. 總結(jié)

開發(fā)者對數(shù)據(jù)和狀態(tài)所做的任何改動,都會被自動且高效的同步到虛擬 DOM(自動同步,體現(xiàn)響應(yīng)式),最后再批量同步到真實 DOM 中,而不是每次改變都去操作一下 DOM(批量同步,體現(xiàn)合并操作)

  • 不需要直接操作控件,通過數(shù)據(jù)驅(qū)動視圖
  • 最大程度降低對最終視圖的修改,提高頁面渲染效率
  • 怎么利用虛擬 DOM?

    1. React

    當(dāng) React UI 渲染時,先渲染一個虛擬 DOM,這是一個輕量的純 js 的對象結(jié)構(gòu),并沒有完全實現(xiàn) DOM,最主要的還是保留了節(jié)點之間的層次關(guān)系和一些基本屬性,因為 DOM 實在是太復(fù)雜,實際在做最后繪制時,這些都是不需要關(guān)心的。所以虛擬 DOM 里每一個節(jié)點只有幾個簡單屬性,哪怕是直接把虛擬 DOM 刪了,根據(jù)新傳進(jìn)來的數(shù)據(jù)重新創(chuàng)建一個新的虛擬 DOM 都非常快。

    當(dāng)有變化時,生成一個新的虛擬 DOM。這個新的虛擬DOM反應(yīng)了數(shù)據(jù)模型的新狀態(tài)。現(xiàn)在我們有 2 個虛擬DOM:新的和老的。對比 DOM 樹差異得到一個 Patch,把這個 Patch 打到真實的 DOM 上去,這有點像版本控制打patch的思路。

    那我們怎么比較出兩顆 DOM 樹的差異呢? Diff 算法!

    即給定任意兩棵樹,找到最少的轉(zhuǎn)換步驟。但是標(biāo)準(zhǔn)的 Diff 算法復(fù)雜度需要 O(n^3),這顯然無法滿足性能要求。Facebook 工程師結(jié)合 Web 界面的特點做出了兩個簡單的假設(shè),使得 Diff 算法復(fù)雜度直接降低到 O(n)。

  • 兩個相同組件產(chǎn)生類似的 DOM 結(jié)構(gòu),不同的組件產(chǎn)生不同的 DOM 結(jié)構(gòu);
  • 對于同一層次的一組子節(jié)點,它們可以通過唯一的 id 進(jìn)行區(qū)分。
  • 算法上的優(yōu)化是 React 整個界面 Render 的基礎(chǔ),事實也證明這兩個假設(shè)是合理而精確的,保證了整體界面構(gòu)建的性能。

    由這一對不同類型的節(jié)點的處理邏輯我們很容易得到推論,那就是 React 的 DOM Diff 算法實際上只會對樹進(jìn)行逐層比較,如下圖:

    React 只會對相同顏色方框內(nèi)的 DOM 節(jié)點進(jìn)行比較,即同一個父節(jié)點下的所有子節(jié)點。當(dāng)發(fā)現(xiàn)節(jié)點已經(jīng)不存在,則該節(jié)點及其子節(jié)點會被完全刪除掉,不會用于進(jìn)一步的比較。這樣只需要對樹進(jìn)行一次遍歷,便能完成整個 DOM 樹的比較。

    實際實踐起來,Diff 算法并沒有這么簡單,感興趣的小伙伴可以在文末的推文去深入了解。

    那跨平臺方案的情況呢?

    2. RN

    上文已經(jīng)提到 RN 是 React 在原生移動應(yīng)用平臺的衍生產(chǎn)物,那兩者主要的區(qū)別是什么呢?主要的區(qū)別在于虛擬 DOM 映射的對象是什么。React 中虛擬 DOM 最終會映射為瀏覽器 DOM 樹,而 RN 中虛擬 DOM 會通過 JavaScriptCore 映射為原生控件樹。

    步驟如下:

  • 布局消息傳遞:將虛擬 DOM 布局信息傳遞給原生;
  • 原生根據(jù)布局信息,映射成對應(yīng)原生控件樹,渲染控件樹。
  • 至此,RN 便實現(xiàn)了跨平臺。

    3. weex

    weex 一定程度上用 JS 實現(xiàn)了 vue 一統(tǒng)天下的效果。

    可以看到,weex 會編譯構(gòu)建虛擬 DOM,并發(fā)送渲染指令給 RenderEngine 層,這樣,同樣一份 JSON 數(shù)據(jù),在不同平臺的渲染引擎中能夠渲染成不同版本的 UI,這是 Weex 可以實現(xiàn)動態(tài)化的原因。

    那三端的語法都不一樣,Weex是怎么統(tǒng)一的?重點在于 JS Framework!

    weex 在 RN 的 JS V8 引擎基礎(chǔ)上,多了 JS Framework 承當(dāng)了重要的職責(zé),它主要負(fù)責(zé):管理 Weex 的生命周期;解析 JS Bundle,轉(zhuǎn)為 Virtual DOM,再通過所在平臺不同的 API 構(gòu)建頁面;進(jìn)行雙向的數(shù)據(jù)交互和響應(yīng)。

    這使得上層具備統(tǒng)一性,在開發(fā)過程中,代碼模式、編譯過程、模板組件、數(shù)據(jù)綁定、生命周期等上層語法是一致的。得益于上層的統(tǒng)一,只需要在 JS Framework 層的最后判斷是由 Vue.js 生成真實的 DOM,還是通過 Native Api 渲染組件即可。

    4. Flutter

    RN 和 React 原理相通,那 Flutter 呢?Flutter Widget 的中心思想是用 Widget 構(gòu)建你的UI(非原生控件)。 那少了原生控件層和 js 層的通信損耗,不需要用虛擬 DOM 了吧?

    非也! Flutter Widget 從 React 中獲得了靈感,也是采用現(xiàn)代響應(yīng)式框架構(gòu)建。

    先看看 Flutter 中三顆重要的樹:

    • Widget 樹:控件樹,表示了我們在 dart 代碼中所寫的控件的結(jié)構(gòu),但這只是描述信息,渲染引擎是不認(rèn)識的。

      Widget 被開發(fā)人員配置了多個屬性來定義它的展現(xiàn)形式,例如配置 Text 組件需要顯示的字符串,配置輸入框組件需要顯示的內(nèi)容……Element 樹會記錄這些配置信息。

    • Element 數(shù):實際控件樹

      在手機屏幕上顯示的控件并非我們在代碼中所寫的 Widget,Flutter 會根據(jù) Widget 樹信息生成控件對應(yīng)的 Element 樹,在 Flutter 中,一個 Widget 通過多次復(fù)用可以對應(yīng)多個 Element 實例,Element 才是我們真正在屏幕上顯示的元素。

      Element 與 Widget 另一個區(qū)別在于,Widget 是不可變的,它的改變就意味著要重建,而其重建也非常頻繁,如果我們將更多的任務(wù)交給它,將會對性能造成很大的損耗,因此我們把 Widget 樹當(dāng)作一個虛擬 DOM 樹,真正被渲染在屏幕上的其實是 ElememtTree,它持有其對應(yīng) Widget 的引用,如果對應(yīng)的 Widget 發(fā)生改變,它就會被標(biāo)記為 dirty Element,下一次更新視圖時根據(jù)這個狀態(tài)只更新被修改的內(nèi)容,這樣就把可變狀態(tài)與 Widget 關(guān)聯(lián)起來,從而達(dá)到提升性能的效果。

    • RenderObject 樹:渲染樹,做組件布局渲染工作,包含渲染搭配、布局約束等信息。

    簡而言之,Flutter 引入虛擬 DOM 的目的是為了確定底層渲染樹從一個狀態(tài)轉(zhuǎn)換到下一個狀態(tài)所需的最小更改。

    虛擬 DOM 對跨平臺技術(shù)的意義

    那分析完各種跨平臺技術(shù),你對虛擬 DOM 有了怎樣的認(rèn)識了呢?

    為什么使用虛擬 DOM?

    是因為快?(實際上不一定快)

    是因為解耦?

    是因為響應(yīng)式?

    對跨平臺技術(shù)來說,更重要的意義在于:

    虛擬 DOM 是 DOM 在內(nèi)存中的一種輕量級表達(dá)方式,是一種統(tǒng)一約定!可以通過不同的渲染引擎生成不同平臺下的 UI!

    虛擬 DOM 的可移植性非常好,這意味著可以渲染到 DOM 以外的任何端,發(fā)揮你的想象力,可以做的事情很多。

    再次審視虛擬 DOM

    虛擬 DOM 真正的價值從來都不是性能,而是不管數(shù)據(jù)怎么變化,都可以用最小的代價來更新 DOM,而且掩蓋了底層的 DOM 操作,讓你用更聲明式的方式來描述你的目的,從而讓你的代碼更容易維護(hù)。

    虛擬 DOM 帶來了很多好思路,打開了通向有趣架構(gòu)的大門,例如將視圖視為狀態(tài)函數(shù)。它讓我們編寫代碼,就像重新呈現(xiàn)整個場景一樣。這不禁讓我感慨,沒有什么是加中間件不能解決的,如果有,那就再加多個中間件。

    5 個詞語概括下意義:

    可維護(hù)性、最小的代價、效率、函數(shù)式UI、數(shù)據(jù)驅(qū)動

    進(jìn)一步思考

    虛擬 DOM 的說明已經(jīng)結(jié)束了,但是對于虛擬 DOM 的思考遠(yuǎn)沒有結(jié)束。

    Rect 的方式有兩大缺點:

  • 每次數(shù)據(jù)更改,哪怕改動很小,都會生成完整的虛擬 DOM,如果 DOM 很復(fù)雜,這個過程就會白白浪費很多計算資源;

  • 比較虛擬 DOM 差異的過程,既慢又容易出錯。因為 React 持有的新舊虛擬 DOM 相互獨立,React 并不知道數(shù)據(jù)源發(fā)生了什么操作,只能根據(jù)兩個虛擬 DOM 來猜測需要執(zhí)行的操作。自動的猜測算法既不準(zhǔn)又慢,必須要前端開發(fā)者手動提供 key 屬性和一些額外的方法實現(xiàn)來幫助 React 猜對。

  • 那么?

    留個思考題,vue 是怎么利用虛擬 DOM 的?針對以上缺點怎么做改進(jìn)?大家可以去了解一下。

    還想了解更多?

    • 別再說虛擬 DOM 快了,要被打臉的
    • 網(wǎng)上都說操作真實 DOM 慢,但測試結(jié)果卻比 React 更快,為什么?
    • 虛擬DOM和Diff算法 - 入門級
    • React的虛擬DOM與diff算法的理解
    • 深度剖析:如何實現(xiàn)一個 Virtual DOM 算法
    • 詳解vue的diff算法
    • 解析vue2.0的diff算法

    本篇完成耗時 26 個番茄鐘( 650分鐘)


    我是 FeelsChaotic,一個寫得了代碼 p 得了圖,剪得了視頻畫得了畫的程序媛,致力于追求代碼優(yōu)雅、架構(gòu)設(shè)計和 T 型成長。

    歡迎關(guān)注 FeelsChaotic 的簡書和掘金,如果我的文章對你哪怕有一點點幫助,歡迎 ??!你的鼓勵是我寫作的最大動力!

    最最重要的,請給出你的建議或意見,有錯誤請多多指正!

    轉(zhuǎn)載于:https://juejin.im/post/5ce7e8fb51882555003dceef

    《新程序員》:云原生和全面數(shù)字化實踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀

    總結(jié)

    以上是生活随笔為你收集整理的从各大跨平台技术说开去,我们真的需要虚拟 DOM 吗?的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。