diff算法并不是由react或者Vue或者虚拟dom提出的一个最新的概念,它是Linux的基础命令,在git代码管理工具中,在网上在线对比文件的工具中都有这个算法的应用,这个算法早就在我们身边了。在vdom中又被用来对比两个虚拟dom的节点,在vdom中应用diff算法是为了找出需要更新的节点,对比的原理和对比字符串是一样的。
vdom使用diff算法的原因: DOM操作是昂贵的,应尽量减少DOM的操作所以需要找出本次DOM必须更新的节点来更新,其它的不更新,找出的过程就需要用到diff算法。
从虚拟dom到真实html节点的核心流程:
patch(container,vnode)
patch(vnode,newVnode)
下面只取简单的情况,真实场景是很复杂的逻辑。
function createElement(vnode) {
var tag = vnode.tag // 'ul'
var attrs = vnode.attrs || {}
var children = vnode.children || []
if (!tag) {
return null
}
// 创建真实的 DOM 元素
var elem = document.createElement(tag)
// 属性
var attrName
for (attrName in attrs) {
if (attrs.hasOwnProperty(attrName)) {
// 给 elem 添加属性
elem.setAttribute(attrName, attrs[attrName])
}
}
// 子元素
children.forEach(function (childVnode) {
// 给 elem 添加子元素
elem.appendChild(createElement(childVnode)) // 递归
})
// 返回真实的 DOM 元素
return elem
}
function updateChildren(vnode, newVnode) {
var children = vnode.children || []
var newChildren = newVnode.children || []
children.forEach(function (childVnode, index) {
var newChildVnode = newChildren[index]
if (childVnode.tag === newChildVnode.tag) {
// 深层次对比,递归
updateChildren(childVnode, newChildVnode)
} else {
// 替换
replaceNode(childVnode, newChildVnode)
}
})
}
function replaceNode(vnode, newVnode) {
var elem = vnode.elem // 真实的 DOM 节点
var newElem = createElement(newVnode)
// 替换
}
还有一些复杂的情况包含:
- 节点新增和删除;
- 节点重新排序;
- 节点属性、样式、事件绑定;
- 如何极致压榨性能;
简单的了解这个算法是为了提高自己的认知能力和工作效率。