前言
我认为diff算法具备两个特点。
一、高效性:有虚拟dom,必然需要diff
算法。通过对比新旧虚拟dom
,将有变化的地方更新在真实dom
上,另外,通过diff
高效的执行比对过程,从而降低时间复杂度为O(n)。
二、必要性:vue2中为了降低watcher
粒度,每个组件只有一个watcher。通过diff
精确找到发生变化的节点,并复用相同的节点。
下面我们通过手写简易diff
算法,来看看具体是怎么实现的吧?
比较新旧虚拟节点
patch(vnode,newVnode)
分为以下几种情况
1.标签名不一样,直接换掉老节点
可以通过vnode.el
属性,获取真实的dom
元素
if(oldVnode.tag !== vnode.tag){
oldVnode.el.parentNode.replaceChild(createElm(vnode),oldVnode.el)
}
复制代码
2.都是文本节点,比较文本内容
如果新老文本不相等,el.textContent=vnode.text
// 如果标签一致但是不存在则是文本节点
if(!oldVnode.tag){
if(oldVnode.text !== vnode.text){
oldVnode.el.textContent = vnode.text;
}
}
复制代码
3.标签一样
复用老真实节点 vnode.el=oldnode.el
1.比较属性 patchProps
let el = vnode.el = oldVnode.el;
function patchProps(vnode, oldProps = {}) { // 初次渲染时可以调用此方法,后续更新也可以调用此方法
let newProps = vnode.data || {};
let el = vnode.el;
// 如果老的属性有,新的没有直接删除
let newStyle = newProps.style || {};
let oldStyle = oldProps.style || {};
for (let key in oldStyle) {
if (!newStyle[key]) { // 新的里面不存在这个样式
el.style[key] = '';
}
}
for (let key in oldProps) {
if (!newProps[key]) {
el.removeAttribute(key);
}
}
// 直接用新的生成到元素上
for (let key in newProps) {
if (key === 'style') {
for (let styleName in newProps.styl