虛擬DOM的作用
首先我們要知道虛擬dom的出現(xiàn)是為了解決什么問(wèn)題的,他解決我們平時(shí)頻繁的直接操作DOM效率低下的問(wèn)題。那么為什么我們直接操作DOM效率會(huì)低下呢?
比如我們創(chuàng)建一個(gè)div,我們可以在控制臺(tái)查看一下這個(gè)div上自帶或者繼承了很多屬性,尤其是我們使用js操作DOM的時(shí)候,我們的DOM本身就很復(fù)雜,js的操作也會(huì)占用很多時(shí)間,但是我們控制不了DOM元素本身,因此虛擬DOM解決的是js操作DOM這一層面,其實(shí)解決的是減少了操作dom的次數(shù)
簡(jiǎn)單實(shí)現(xiàn)虛擬DOM
虛擬DOM,見(jiàn)名知意,就是假的DOM,我們真實(shí)的DOM掛載在頁(yè)面上的,而我們的虛擬DOM則是在內(nèi)存中的。這個(gè)就需要我們把真實(shí)的DOM抽象成一個(gè)對(duì)象放在內(nèi)存中。這個(gè)對(duì)象就可以是如下類(lèi)型:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
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)造出這樣的對(duì)象可以自己封裝一個(gè)構(gòu)造函數(shù)如下:
1
2
3
4
5
|
function Element(tagName, props, children) { this .tagName = tagName this .props = props this .children = children } |
有了這個(gè)對(duì)象,我們需要把這個(gè)虛擬DOM渲染到真實(shí)DOM上,可以寫(xiě)出如下方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
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出這個(gè)對(duì)象調(diào)用render()方法然后appendChild到body中就好了:
1
2
3
4
5
6
7
8
|
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)存中又會(huì)產(chǎn)生新的虛擬DOM,如果我們直接用這個(gè)新的虛擬DOM結(jié)構(gòu)的話,又會(huì)導(dǎo)致很多重復(fù)的渲染,因此 這個(gè)時(shí)候diff算法的作用就體現(xiàn)了出來(lái),diff通過(guò)比較新舊兩個(gè)虛擬DOM樹(shù),找出差異,并且記錄下來(lái),然后把記錄的差異應(yīng)用到真實(shí)的DOM樹(shù)上。
原理:
diff算法通過(guò)對(duì)新舊兩顆樹(shù)進(jìn)行深度優(yōu)先遍歷,每一個(gè)節(jié)點(diǎn)都加一個(gè)唯一的標(biāo)識(shí)。
這個(gè)過(guò)程分為2步
- 找出兩個(gè)樹(shù)的差異,并記錄在一個(gè)偽數(shù)組里。
- 把這些不同應(yīng)用到真實(shí)的DOM樹(shù)上
對(duì)于dom的操作基本可化為4種類(lèi)型
- 對(duì)節(jié)點(diǎn)的刪除,移動(dòng),添加子節(jié)點(diǎn)
- 更換節(jié)點(diǎn)標(biāo)簽
- 對(duì)于文本節(jié)點(diǎn),修改節(jié)點(diǎn)文本
- 修改節(jié)點(diǎn)props
下面會(huì)用偽代碼的形式大致過(guò)一下這個(gè)流程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
|
// diff 函數(shù),對(duì)比兩棵樹(shù) function diff(oldTree, newTree) { var patchs = {}; // 偽數(shù)組,記錄差異 // 對(duì)4種節(jié)點(diǎn)做錯(cuò)判斷 dfWork(oldTree, newTree, patchs, index) return patchs } function dfWork(oldTree, newTree, patchs, index) { let currentPatch = [] if (1) { // 對(duì)節(jié)點(diǎn)的刪除 currentPatch.push() } else if (3) { // 對(duì)節(jié)點(diǎn)的文本的更換 currentPatch.push() } else { // 修改節(jié)點(diǎn)的props 對(duì)children的檢查 // 對(duì)props作diff算法,把變化記錄到patchs中。 currentPatch.push({ type: patch.PROPS, props: propsPatches }) // 然后需要對(duì)子節(jié)點(diǎn)作diff算法 diffChildren(oldNode.children, newNode.children, index, patches, currentPatch) } } function diffChildren(oldChildren, newChildren, index, patches, currentPatch) { // 對(duì)子節(jié)點(diǎn)作diff算法,遍歷子節(jié)點(diǎn),遞歸調(diào)用dfWork,做差異得到patchs } // 把變化應(yīng)用在真實(shí)的DOM樹(shù)上 function patch(node, patchs) { // node為老的DOM樹(shù),patchs變化。 // 我們會(huì)遍歷這個(gè)patchs,并且把node和patch對(duì)應(yīng)上, } function applyPatch(node, patchs) { // 應(yīng)為每個(gè)節(jié)點(diǎn)可能有多個(gè)變化,所以也需要遍歷 switch (patchs.type) { case REPLACE: // 節(jié)點(diǎn)替換 // node.render() break ; case REORDER: // 節(jié)點(diǎn)的移動(dòng)刪除新增子節(jié)點(diǎn)。 break ; case PROPS: // setProps break ; case TEXT: // 對(duì)節(jié)點(diǎn)文本的修改 // node.nodeValue break ; default : break ; } } |
參考文檔:深度剖析:如何實(shí)現(xiàn)一個(gè) Virtual DOM 算法 作者:livoras,內(nèi)置源碼。
到此這篇關(guān)于react中的虛擬dom和diff算法的文章就介紹到這了,更多相關(guān)react虛擬dom和diff算法內(nèi)容請(qǐng)搜索服務(wù)器之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持服務(wù)器之家!
原文鏈接:https://blog.csdn.net/qq_44983621/article/details/115733067