摘要:具體而言,就是每次數(shù)據(jù)發(fā)生變化,就重新執(zhí)行一次整體渲染。而給出了解決方案,就是。由于只關(guān)注,通過(guò)閱讀兩個(gè)庫(kù)的源碼,對(duì)于的定位有了更深一步的理解。第二個(gè)而且,技術(shù)本身不是目的,能夠更好地解決問(wèn)題才是王道嘛。
前言
React 好像已經(jīng)火了很久很久,以致于我們對(duì)于 Virtual DOM 這個(gè)詞都已經(jīng)很熟悉了,網(wǎng)上也有非常多的介紹 React、Virtual DOM 的文章。但是直到前不久我專(zhuān)門(mén)花時(shí)間去學(xué)習(xí) Virtual DOM,才讓我對(duì) Virtual DOM 有了一定的理解,以致于要懷疑起很久之前看過(guò)的那些文章來(lái)。倒不是這些文章講得不對(duì),而是現(xiàn)在在我看來(lái)角度不太好,說(shuō)得越多,越說(shuō)不清。
讓我能夠有所開(kāi)竅(自認(rèn)為)的,是這篇文章:
Change And Its Detection In JavaScript Frameworks
Monday Mar 2, 2015 by Tero Parviainen
http://teropa.info/blog/2015/...
作者看問(wèn)題的角度很棒,從數(shù)據(jù)變更與UI同步的角度來(lái)介紹各個(gè)典型框架,特別是對(duì)于 React 的 Virtual DOM,從這個(gè)角度理解起來(lái)更容易些。
感興趣的同學(xué),如果沒(méi)有讀過(guò)這篇文章,推薦去看一看,不感興趣就算了。不過(guò)接下來(lái)我要講的東西,部分整理自這篇文章,特別是從這篇文章中引用的圖片,非常棒。當(dāng)然還有我自己的一些思考,以及一些對(duì)于目前 Virtual DOM 實(shí)現(xiàn)的開(kāi)源庫(kù)的分析。
如果讀了上面推薦的這篇文章,我倒是不介意你不再繼續(xù)把本文讀下去,因?yàn)橛行〇|西你已經(jīng)領(lǐng)會(huì)到了。當(dāng)然,也不反對(duì)。
變化這件事談?wù)擁?yè)面的變化之前,咱們先看下數(shù)據(jù)和頁(yè)面(視覺(jué)層面的頁(yè)面)的關(guān)系。數(shù)據(jù)是隱藏在頁(yè)面底下,通過(guò)渲染展示給用戶。同樣的數(shù)據(jù),按照不同的頁(yè)面設(shè)計(jì)和實(shí)現(xiàn),會(huì)以不同形式、樣式的頁(yè)面呈現(xiàn)出來(lái)。有時(shí)候在一個(gè)頁(yè)面內(nèi)的不同位置,也會(huì)有相同數(shù)據(jù)的不同表現(xiàn)。
Web 的早期,這些頁(yè)面通常是靜態(tài)的,頁(yè)面內(nèi)容不會(huì)變化。而如果數(shù)據(jù)發(fā)生了變化,通常需要重新請(qǐng)求頁(yè)面,得到基于新的數(shù)據(jù)渲染出的新的頁(yè)面。
至少,這個(gè)模式理解起來(lái)挺簡(jiǎn)單不是嗎。
直到 Web 應(yīng)用復(fù)雜起來(lái),開(kāi)發(fā)者們開(kāi)始關(guān)注用戶體驗(yàn),開(kāi)始將大量的處理向前端遷移,頁(yè)面變得動(dòng)態(tài)、靈活起來(lái)。一個(gè)顯著的特征是,數(shù)據(jù)發(fā)生變化之后,不再需要刷新頁(yè)面就能看到頁(yè)面上的內(nèi)容隨之更新了。
前端需要做的事情變得多了起來(lái),前端工程師們也就修煉了起來(lái),各種前端技術(shù)也就出現(xiàn)了。
首先,聰明的工程師們發(fā)現(xiàn)既然是在前端渲染頁(yè)面,如果只是部分?jǐn)?shù)據(jù)發(fā)生了變化,就要把頁(yè)面整體或一大塊區(qū)域重新渲染就有點(diǎn)笨了。為什么不把事情做得更極致些,只更新變化的數(shù)據(jù)對(duì)應(yīng)的頁(yè)面的內(nèi)容呢?
怎么做呢?操作 DOM 唄。DOM 就是瀏覽器提供給開(kāi)發(fā)者用于操作頁(yè)面的模型嘛,直接通過(guò)腳本來(lái)調(diào)用 DOM 的各種接口就 OK 了。而且我們還有了像 jQuery 這樣的棒棒的工具,操作 DOM 變得 so easy。
然而,頁(yè)面越來(lái)越復(fù)雜,聰明的工程師們發(fā)現(xiàn)數(shù)據(jù)變化之后,老是需要手動(dòng)編碼去操作對(duì)應(yīng)的 DOM 節(jié)點(diǎn)執(zhí)行更新,有點(diǎn)煩,不夠懶啊。于是各種框架如雨后春筍般出現(xiàn)了,紛紛表示可以簡(jiǎn)化這個(gè)過(guò)程。
稍微早期的框架有這樣的:
開(kāi)發(fā)者借助框架,監(jiān)聽(tīng)數(shù)據(jù)的變更,在數(shù)據(jù)變更后更新對(duì)應(yīng)的 DOM 節(jié)點(diǎn)。雖然還是要寫(xiě)一些代碼,但是寫(xiě)出來(lái)的代碼好像很有條理的樣子,至少更容易理解和維護(hù)了,也不錯(cuò)嘛。
更進(jìn)一步,MVVM 框架出現(xiàn)了,以 AngularJS 為代表:
仍然是數(shù)據(jù)變化后更新對(duì)應(yīng) DOM 節(jié)點(diǎn)的方式,但是建立這種綁定關(guān)系的過(guò)程被框架所處理,開(kāi)發(fā)者要寫(xiě)的代碼變少了,而且代碼更易讀和維護(hù)了。
再然后呢,大家就在這個(gè)棒棒的模式上繼續(xù)深耕,紛紛表示還可以在性能上做得更好,前端領(lǐng)域一片繁榮。
再后來(lái) React 出現(xiàn)了,它不僅不是 MVVM 框架,甚至連 MV 框架都不是。這年頭,不是個(gè) MV 框架還好意思出門(mén)?可 React 還真的帶來(lái)了新的思路!
什么思路呢?
就是回到過(guò)去,回到那個(gè)簡(jiǎn)單而美好的時(shí)候。具體而言,就是每次數(shù)據(jù)發(fā)生變化,就重新執(zhí)行一次整體渲染。的確這樣更簡(jiǎn)單,不用去琢磨到底是數(shù)據(jù)的哪一部分變化了,需要更新頁(yè)面的哪一部分。但是壞處太明顯,體驗(yàn)不好啊。而 React 給出了解決方案,就是 Virtual DOM。
Virtual DOM 概況來(lái)講,就是在數(shù)據(jù)和真實(shí) DOM 之間建立了一層緩沖。對(duì)于開(kāi)發(fā)者而言,數(shù)據(jù)變化了就調(diào)用 React 的渲染方法,而 React 并不是直接得到新的 DOM 進(jìn)行替換,而是先生成 Virtual DOM,與上一次渲染得到的 Virtual DOM 進(jìn)行比對(duì),在渲染得到的 Virtual DOM 上發(fā)現(xiàn)變化,然后將變化的地方更新到真實(shí) DOM 上。
簡(jiǎn)單來(lái)說(shuō),React 在提供給開(kāi)發(fā)者簡(jiǎn)單的開(kāi)發(fā)模式的情況下,借助 Virtual DOM 實(shí)現(xiàn)了性能上的優(yōu)化,以致于敢說(shuō)自己“不慢”。
Virtual DOMReact 基于 Virtual DOM 的數(shù)據(jù)更新與UI同步機(jī)制:
初始渲染時(shí),首先將數(shù)據(jù)渲染為 Virtual DOM,然后由 Virtual DOM 生成 DOM。
數(shù)據(jù)更新時(shí),渲染得到新的 Virtual DOM,與上一次得到的 Virtual DOM 進(jìn)行 diff,得到所有需要在 DOM 上進(jìn)行的變更,然后在 patch 過(guò)程中應(yīng)用到 DOM 上實(shí)現(xiàn)UI的同步更新。
Virtual DOM 作為數(shù)據(jù)結(jié)構(gòu),需要能準(zhǔn)確地轉(zhuǎn)換為真實(shí) DOM,并且方便進(jìn)行對(duì)比。除了 Virtual DOM 外,React 還實(shí)現(xiàn)了其他的特性,為了專(zhuān)注于 Virtual DOM,我另外找了兩個(gè)比較 Virtual DOM 來(lái)學(xué)習(xí):
virtual-dom
Snabbdom
這里也推薦給感興趣且還沒(méi)有讀過(guò)兩個(gè)庫(kù)源碼的同學(xué)。
由于只關(guān)注 Virtual DOM,通過(guò)閱讀兩個(gè)庫(kù)的源碼,對(duì)于 Virtual DOM 的定位有了更深一步的理解。
首先看數(shù)據(jù)結(jié)構(gòu)。
Virtual DOM 數(shù)據(jù)結(jié)構(gòu)
DOM 通常被視為一棵樹(shù),元素則是這棵樹(shù)上的節(jié)點(diǎn)(node),而 Virtual DOM 的基礎(chǔ),就是 Virtual Node 了。
在 virtual-dom 中,給 Virtual Node 聲明了對(duì)應(yīng)的類(lèi) VirtualNode,基本是用于存儲(chǔ)數(shù)據(jù),包括:
tagName
properties
children
key
namespace
count
hasWidgets
hasThunks
hooks
descendantHooks
Snabbdom 的 Virtual Node 則是純數(shù)據(jù)對(duì)象,通過(guò) vnode 模塊來(lái)創(chuàng)建,對(duì)象屬性包括:
sel
data
children
text
elm
key
雖然有所差別,除去實(shí)現(xiàn)上的差別和庫(kù)本身的額外特性,可以看到 Virtual Node 用于創(chuàng)建真實(shí)節(jié)點(diǎn)的數(shù)據(jù)包括:
元素類(lèi)型
元素屬性
元素的子節(jié)點(diǎn)
有了這些其實(shí)就可以創(chuàng)建對(duì)應(yīng)的真實(shí)節(jié)點(diǎn)了。
創(chuàng)建 Virtual DOM
嵌套 Virtual Node 就可以得到一棵樹(shù)了。virtual-dom 和 Snabbdom 都提供了函數(shù)調(diào)用的方式來(lái)創(chuàng)建 Virtual Tree,這個(gè)過(guò)程就是渲染了:
var vTree = h("div", [ h("span", "hello"), h("span", "world") ])
React 提供 JSX 這顆糖,使得我們可以用類(lèi)似 HTML 的語(yǔ)法來(lái)編寫(xiě),不過(guò)編譯后實(shí)質(zhì)還是通過(guò)函數(shù)調(diào)用來(lái)得到一棵嵌套的 Virtual Tree。而且這對(duì)于理解 Virtual DOM 機(jī)制來(lái)說(shuō)不是特別重要,先不管這個(gè)。
使用 Virtual DOM
首先來(lái)看初始化,virtual-dom 提供了 createElement 函數(shù):
var rootNode = createElement(tree) document.body.appendChild(rootNode)
根據(jù) Virtual Node 創(chuàng)建真實(shí) DOM 元素,然后再追加到頁(yè)面上。
再來(lái)看更新。virtual-dom 有明確的兩步操作,首先 diff,然后 patch:
var newTree = render(count) var patches = diff(tree, newTree) rootNode = patch(rootNode, patches)
而 Snabbdom 則簡(jiǎn)單些,只有一個(gè) patch 函數(shù),內(nèi)部在進(jìn)行比對(duì)的同時(shí)將更新應(yīng)用到了真實(shí) DOM 上,而且初始化也是用的 patch 函數(shù):
var vnode = render(data) var container = document.getElementById("container") patch(container, vnode) // after data changed var newVnode = render(data) patch(vnode, newVnode)
性能優(yōu)化
關(guān)于性能優(yōu)化,除了 Virtual DOM 機(jī)制本身提供的特性以外,再就是不同的 Virtual DOM 庫(kù)自身的優(yōu)化方案了,這個(gè)可以看上面兩個(gè)庫(kù)的文檔,不再贅述。
其實(shí)提到 Virtual DOM 的差異比對(duì),有人會(huì)對(duì)其內(nèi)部如何處理數(shù)組感興趣。的確,如果數(shù)組元素的位置發(fā)生了改變,這個(gè)要識(shí)別起來(lái)是有點(diǎn)麻煩。為此,上面兩個(gè)庫(kù)和 React 其實(shí)都在 Virtual Node 上額外記錄了一個(gè)屬性“key”,就是用來(lái)輔助進(jìn)行 Virtual Node 的比對(duì)的。
簡(jiǎn)單來(lái)說(shuō),如果兩個(gè) Virtual Node 的位置不同,但是 key 屬性相同,那么會(huì)將這兩個(gè)節(jié)點(diǎn)視為由相同數(shù)據(jù)渲染得到的,然后進(jìn)一步進(jìn)行差異分析。所以,并不是僅僅按照位置進(jìn)行比對(duì),具體的實(shí)現(xiàn)可以查看各個(gè)庫(kù)的源碼。
小結(jié)OK,以上就是我要講的全部所有內(nèi)容了。
相信很多同學(xué)之前對(duì) Virtual DOM 已經(jīng)很熟悉了,比我理解得更深入的同學(xué)相信也不會(huì)少。不過(guò)從“數(shù)據(jù)變化與UI同步更新”這個(gè)角度來(lái)理解 Virtual DOM,在我看來(lái)是比較好的,所以整理在這里了。
有個(gè)問(wèn)題挺常見(jiàn),AngularJS 和 React 哪個(gè)更好?
如果說(shuō)各有千秋的話,估計(jì)大家就“呵呵”了。但是這兩個(gè)框架/庫(kù)從“數(shù)據(jù)變化與UI同步更新”的角度來(lái)看,的確都解決了問(wèn)題,而且解決問(wèn)題的方式大家都挺認(rèn)可(至少在喜歡它們的同學(xué)眼里是這樣的)。
而且,如果大家關(guān)注 Vue 的話,可以看到,這個(gè) MVVM 框架已經(jīng)發(fā)布了 2.0,其中就采用了 Virtual DOM 實(shí)現(xiàn)其UI同步更新!所以,這的確不矛盾啊。
第二個(gè)而且,技術(shù)本身不是目的,能夠更好地解決問(wèn)題才是王道嘛。
原文:一起理解 Virtual DOM
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/91233.html
摘要:借鑒,等的思想,引入狀態(tài)管理在狀態(tài)管理方面借鑒的思想,實(shí)現(xiàn)了單向數(shù)據(jù)流的管理。引入和算法總所周知,由于元素的龐大,以及操作容易引起頁(yè)面重排的原因,直接操作性能是非常非常差的。然后通過(guò)算法對(duì)比新舊虛擬樹(shù),對(duì)真實(shí)進(jìn)行最小單位的修改。 最近筆者打算自己造一個(gè)輪子,寫(xiě)一個(gè)mvvm的庫(kù),鍛煉自己的能力,畢竟用庫(kù)誰(shuí)都可以,但是開(kāi)發(fā)庫(kù)的能力不是誰(shuí)都有,不過(guò)筆者不是什么大神,所以更多的是希望在這個(gè)過(guò)程...
摘要:二原理每個(gè)都有兩個(gè),一個(gè)是新的,一個(gè)是原來(lái)的。三實(shí)現(xiàn)過(guò)程四算法的理解與實(shí)現(xiàn)本質(zhì)上就是在和之間做了一個(gè)緩存。將差異的應(yīng)用到真正的樹(shù)上對(duì)真實(shí)上的樹(shù)進(jìn)行深度優(yōu)先遍歷,在所有的差異列表中找出當(dāng)前遍歷的節(jié)點(diǎn)差異,然后根據(jù)不同進(jìn)行操作。 React Virtual DOM 一、概念 在react中,對(duì)于每個(gè)DOM對(duì)象都有一個(gè)相應(yīng)的虛擬DOM對(duì)象,相當(dāng)于DOM對(duì)象的輕量級(jí)副本 由于是Virtual...
摘要:關(guān)于前端框架大家都有了解,或多或少的使用過(guò),比如,,等等。那么你是否也想自己手寫(xiě)一個(gè)的前端框架呢,我們從入手,手把手教你寫(xiě)基于的前端框架,在整個(gè)編寫(xiě)的過(guò)程中,希望大家學(xué)習(xí)更多,理解更多。本節(jié)我們以打包工具結(jié)合轉(zhuǎn)換插件實(shí)現(xiàn)數(shù)據(jù)的抽象。 關(guān)于MVVM前端框架大家都有了解,或多或少的使用過(guò),比如Angular,React,VUE等等。那么你是否也想自己手寫(xiě)一個(gè)MVVM的前端框架呢,我們從Vi...
摘要:既然看不懂,那就看看社區(qū)前輩們寫(xiě)的一些源碼分析文章以及實(shí)現(xiàn)思路吧,又這么過(guò)了幾天,總算是摸清點(diǎn)思路,于是在參考了前輩們的基礎(chǔ)上,實(shí)現(xiàn)了一個(gè)簡(jiǎn)易版的。總結(jié)以上就是實(shí)現(xiàn)一個(gè)的總體思路,下節(jié)我們重點(diǎn)放在不同的上。 寫(xiě)在開(kāi)頭 工作中使用react也很長(zhǎng)一段時(shí)間了,雖然對(duì)它的用法,原理有了一定的了解,但是總感覺(jué)停留在表面。本著知其然知其所以然的態(tài)度,我試著去看了react源碼,幾天下來(lái),發(fā)現(xiàn)并不...
閱讀 865·2023-04-26 00:30
閱讀 2780·2021-11-23 09:51
閱讀 1118·2021-11-02 14:38
閱讀 2775·2021-09-07 10:23
閱讀 2333·2021-08-21 14:09
閱讀 1536·2019-08-30 10:57
閱讀 1666·2019-08-29 11:20
閱讀 1208·2019-08-26 13:53