学习底层的虚拟DOM原理
二话不说,直接上代码
基本的代码如下:
let nodeData = {
tag: 'div',
children: [{
tag: 'p',
children: [{
tag: 'span',
children: [{
tag: '#text',
text: 'wushao.xyz'
}]
}]
},
{
tag: 'span',
children: [{
tag: '#text',
text: 'wushao.com'
}]
}
]
}
/*
等同于这段html代码
<div>
<p>
<span>wushao.xyz</span>
</p>
<span>wushao.com</span>
</div>
*/
/**
* ES6的写法
*/
class VNode {
constructor(tag, children, text) {
this.tag = tag
this.children = children
this.text = text
}
render() {
if (this.tag === '#text') {
return document.createTextNode(this.text)
}
let el = document.createElement(this.tag)
this.children.forEach(vChild => {
el.appendChild(vChild.render())
});
return el
}
}
/**
* 创建虚拟dom节点的简便函数
* @param {标签名} tag
* @param {子标签} children
* @param {文本值} text
*/
function v(tag, children, text) {
//如果没有tag,第二个参数是字符串的话
if (typeof children === 'string') {
text = children
children = []
}
return new VNode(tag, children, text)
}
/**
* ES5的写法
* @param {*} tag
* @param {*} children
* @param {*} text
*/
function vNode(tag, children, text) {
this.tag = tag
this.children = children
this.text = text
}
vNode.prototype.render = function () {
if (this.tag === '#text') {
return document.createTextNode(this.text)
}
let el = document.createElement(this.tag)
this.children.forEach(vChild => {
el.appendChild(vChild.render())
});
return el
}
//举例说明
let vNode1 = v('div', [
v('p', [
v('span', [v('#text', 'wushao.xyz')])
]),
v('span', [
v('#text', 'wushao.com')
])
])
console.log(vNode1);
const root = document.querySelector('#root')
root.appendChild(vNode1.render()) //把虚拟的dom映射进了真实的dom结构里面
let vNode2 = v('div', [
v('p', [
v('span', [v('#text', 'wushao.xyz')])
]),
v('span', [
v('#text', 'wushao.com')
]),
v('span', [
v('#text', 'wushao')
])
])
document.querySelector('#change').onclick = function () {
root.innerHTML = ''
root.appendChild(vNode2.render())
}
/**
* 比较前后两个节点
* @param {要比较的DOM结构} parent
* @param {旧的节点} newVNode
* @param {*新的节点 oldNode
* @param {索引} index
*/
function patchElement(parent, newVNode, oldNode, index = 0) {
if (!oldNode) {
//新增元素
parent.appendChild(newVNode.render())
} else if (!newVNode) {
parent.removeChild(parent.childNodes[index])
} else if (newVNode.tag !== oldNode.tag || newVNode.text != oldNode.text){
parent.replaceChild(newVNode.render(), parent.childNodes[index])
} else {
for (let i = 0; i < newVNode.children.length || i < oldNode.children.length; i++) {
patchElement(parent.childNodes[index], newVNode.children[i], oldNode.children[i], i)
}
}
}
可以在HTML
中直接测试
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>虚拟DOM</title>
</head>
<body>
<div id="root">
</div>
<button id="change">change</button>
<script src="./v-dom.js"></script>
</body>
</html>