react中的虛擬dom和diff算法詳解
虛擬DOM的作用
首先我們要知道虛擬dom的出現(xiàn)是為了解決什么問題的,他解決我們平時頻繁的直接操作DOM效率低下的問題。那么為什么我們直接操作DOM效率會低下呢?
比如我們創(chuàng)建一個div,我們可以在控制臺查看一下這個div上自帶或者繼承了很多屬性,尤其是我們使用js操作DOM的時候,我們的DOM本身就很復(fù)雜,js的操作也會占用很多時間,但是我們控制不了DOM元素本身,因此虛擬DOM解決的是js操作DOM這一層面,其實(shí)解決的是減少了操作dom的次數(shù)
簡單實(shí)現(xiàn)虛擬DOM
虛擬DOM,見名知意,就是假的DOM,我們真實(shí)的DOM掛載在頁面上的,而我們的虛擬DOM則是在內(nèi)存中的。這個就需要我們把真實(shí)的DOM抽象成一個對象放在內(nèi)存中。這個對象就可以是如下類型:
var element = {
tagName: 'div',
props: {
class: 'box'
},
children: {
{
tagName: 'p',
props: {
class: 'p1'
},
children: ['我是p1']
},
{
tagName: 'p',
props: {
class: 'p2'
},
children: ['我是p2']
},
{
tagName: 'p',
props: {
class: 'p3'
},
children: ['我是p3']
},
}
}
我們想要構(gòu)造出這樣的對象可以自己封裝一個構(gòu)造函數(shù)如下:
function Element(tagName, props, children) {
this.tagName = tagName
this.props = props
this.children = children
}
有了這個對象,我們需要把這個虛擬DOM渲染到真實(shí)DOM上,可以寫出如下方法:
Element.prototype.render = function () {
const { tagName, props, children } = this
var el = document.createElement(tagName)
for (key in props) {
el.setAttribute(key, props[key])
}
children.forEach((item) => {
const childEl = (item instanceof Element) ?
item.render() :
document.createTextNode(item)
el.appendChild(childEl)
})
return el
}
最后我們可以new出這個對象調(diào)用render()方法然后appendChild到body中就好了:
let virtualDom = new Element('div', { class: 'box' }, [
new Element('p', { class: 'p1' }, ['我是p1']),
new Element('p', { class: 'p2' }, ['我是p2']),
new Element('p', { class: 'p3' }, ['我是p3']),
])
let a = virtualDom.render()
document.body.appendChild(a)
diff算法
首先我們先了解一下diff算法的作用
如果我們的虛擬dom發(fā)生了變化,我們的內(nèi)存中又會產(chǎn)生新的虛擬DOM,如果我們直接用這個新的虛擬DOM結(jié)構(gòu)的話,又會導(dǎo)致很多重復(fù)的渲染,因此 這個時候diff算法的作用就體現(xiàn)了出來,diff通過比較新舊兩個虛擬DOM樹,找出差異,并且記錄下來,然后把記錄的差異應(yīng)用到真實(shí)的DOM樹上。
原理:
diff算法通過對新舊兩顆樹進(jìn)行深度優(yōu)先遍歷,每一個節(jié)點(diǎn)都加一個唯一的標(biāo)識。
這個過程分為2步
- 找出兩個樹的差異,并記錄在一個偽數(shù)組里。
- 把這些不同應(yīng)用到真實(shí)的DOM樹上
對于dom的操作基本可化為4種類型
- 對節(jié)點(diǎn)的刪除,移動,添加子節(jié)點(diǎn)
- 更換節(jié)點(diǎn)標(biāo)簽
- 對于文本節(jié)點(diǎn),修改節(jié)點(diǎn)文本
- 修改節(jié)點(diǎn)props
下面會用偽代碼的形式大致過一下這個流程
// diff 函數(shù),對比兩棵樹
function diff(oldTree, newTree) {
var patchs = {}; // 偽數(shù)組,記錄差異
// 對4種節(jié)點(diǎn)做錯判斷
dfWork(oldTree, newTree, patchs, index)
return patchs
}
function dfWork(oldTree, newTree, patchs, index) {
let currentPatch = []
if (1) { // 對節(jié)點(diǎn)的刪除
currentPatch.push()
} else if (3) { // 對節(jié)點(diǎn)的文本的更換
currentPatch.push()
} else { // 修改節(jié)點(diǎn)的props 對children的檢查
// 對props作diff算法,把變化記錄到patchs中。
currentPatch.push({ type: patch.PROPS, props: propsPatches })
// 然后需要對子節(jié)點(diǎn)作diff算法
diffChildren(oldNode.children, newNode.children, index, patches, currentPatch)
}
}
function diffChildren(oldChildren, newChildren, index, patches, currentPatch) {
// 對子節(jié)點(diǎn)作diff算法,遍歷子節(jié)點(diǎn),遞歸調(diào)用dfWork,做差異得到patchs
}
// 把變化應(yīng)用在真實(shí)的DOM樹上
function patch(node, patchs) {
// node為老的DOM樹,patchs變化。
// 我們會遍歷這個patchs,并且把node和patch對應(yīng)上,
}
function applyPatch(node, patchs) {
// 應(yīng)為每個節(jié)點(diǎn)可能有多個變化,所以也需要遍歷
switch (patchs.type) {
case REPLACE: // 節(jié)點(diǎn)替換
// node.render()
break;
case REORDER: // 節(jié)點(diǎn)的移動刪除新增子節(jié)點(diǎn)。
break;
case PROPS:
// setProps
break;
case TEXT: // 對節(jié)點(diǎn)文本的修改
// node.nodeValue
break;
default:
break;
}
}
參考文檔:深度剖析:如何實(shí)現(xiàn)一個 Virtual DOM 算法 作者:livoras,內(nèi)置源碼。
到此這篇關(guān)于react中的虛擬dom和diff算法的文章就介紹到這了,更多相關(guān)react虛擬dom和diff算法內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Docker部署SpringBoot項(xiàng)目到云服務(wù)器的實(shí)現(xiàn)步驟
Docker作為一種輕量級的容器化技術(shù),為開發(fā)者提供了快速、便捷的部署方案,本文主要介紹了Docker部署SpringBoot項(xiàng)目到云服務(wù)器,具有一定的參考價(jià)值,感興趣的可以了解一下2024-01-01
React報(bào)錯之Object?is?possibly?null的問題及解決方法
這篇文章主要介紹了React報(bào)錯之Object?is?possibly?null的問題,造成 "Object is possibly null"的錯誤是因?yàn)閡seRef()鉤子可以傳遞一個初始值作為參數(shù),而我們傳遞null作為初始值,本文給大家分享詳細(xì)解決方法,需要的朋友可以參考下2022-07-07
通過React-Native實(shí)現(xiàn)自定義橫向滑動進(jìn)度條的 ScrollView組件
開發(fā)一個首頁擺放菜單入口的ScrollView可滑動組件,允許自定義橫向滑動進(jìn)度條,且內(nèi)部渲染的菜單內(nèi)容支持自定義展示的行數(shù)和列數(shù),在內(nèi)容超出屏幕后,渲染順序?yàn)榭v向由上至下依次排列,對React Native橫向滑動進(jìn)度條相關(guān)知識感興趣的朋友一起看看吧2024-02-02
詳解React+Koa實(shí)現(xiàn)服務(wù)端渲染(SSR)
這篇文章主要介紹了詳解React+Koa實(shí)現(xiàn)服務(wù)端渲染(SSR),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-05-05
react?echarts?tree樹圖搜索展開功能示例詳解
這篇文章主要為大家介紹了react?echarts?tree樹圖搜索展開功能示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01

