真实dom和虚拟dom对比,也就是将真实dom转化为一个js对象,这个js对象就是真实dom的相关描述:
前后虚拟dom进行diff找到最小量更新,然后再用patch方法将变化更新到真实dom上:
简单来说,diff算法是根据beforeUpdate和updated前后改变的虚拟dom进行精细化比对找到虚拟dom的改变,然后使用patch将最小量改变更新到真实的dom树上。
官方snabbdom库中的一些函数的操作演示:
一个虚拟节点有哪些属性:
首先需要初始化init一个用于将虚拟dom上树的patch函数,然后通过h函数创建虚拟节点,最后将创建的虚拟节点使用patch函数将其上树的对应的container容器中。
dom上树(虚拟dom渲染为真实dom)结果:
h函数创建的虚拟dom:
dom上树(虚拟dom渲染为真实dom)结果:
这里创建一个自己写的snabbdom库mySnabbdom,其需要实现手写一个阉割版即简易版的h函数、patch函数等。
根据传入的参数创建虚拟节点:vnode.js:
用于创建虚拟dom树的h函数:h.js:
所以说diff算法不是真的无微不至,不会考虑上面所说的几种情况即同一个虚拟节点(选择器相同和key完全相同)和不同层的虚拟dom不会进行diff,但这并不影响性能,因为在实际开发中,基本不会遇见这几种情形,所以这是合理的优化机制。
####1.2新旧虚拟dom是同一个节点时,进行diff最小量更新
diff精细化比较的过程:
最难的就是五角星框里面内容的实现。
尝试手写,但是执行不顺畅
在进行精细化diff比较新旧dom节点下子节点children时,新创建的节点要插入到所有未处理的节点之前,而不是所有已处理节点之后。
//此代码是尝试手写diff精细化比较算法,但思路比较复杂,还是需要使用官方提供的思路,通过使用四个指针新前、新后、旧前、旧后来实现虚拟dom更新。
四中命中查找,按顺序进行命中,如果当前子节点命中一种就不用再让当前子节点接着往下寻找命中了,下一步操作是让当前节点的下一个节点从头开始查找命中
新前指代新虚拟节点的子节点中没有处理的开头的节点
新后指代新虚拟节点的子节点中的没有处理节点的最后一个节点
旧前指代旧虚拟节点的子节点中没有处理的开头的节点
旧后指代旧虚拟节点的子节点中没有处理节点的最后一个节点
新虚拟dom子节点一共含有以几种情况:
updateChildren.js为新前旧前、新后旧后、新后旧前、新前旧后的子节点更新策略(重要!):
console.log("新节点数组中还有剩余节点没有处理,要加项。把所有剩余的节点,都要插入到oldStartIndex之前");
h.js文件:用于根据真实dom创建一个虚拟dom对象:
updateChildren.js为新前旧前、新后旧后、新后旧前、新前旧后的子节点更新策略(重要!):
console.log("新节点数组中还有剩余节点没有处理,要加项。把所有剩余的节点,都要插入到oldStartIndex之前");首先进入patch方法,新旧vnode进行diff。如果旧节点为dom对象,那么需要将其封装为虚拟dom对象,再与新的vnode进行比较;如果新旧vnode不是同一个节点(选择器sel和vnode唯一标识符key不相同),那么就直接暴力拆除旧得dom,然后插入新的dom;另一方面如果新旧虚拟dom是同一个节点(即sel属性和key全都相同),那么开始进行diff精细化比较;首先如果新旧节点是同一个对象的话直接return返回不用处理;然后再看新vnode是否是文本节点即不含有children,如果是则直接让旧节点的innerHTML=newVnode.text来覆盖旧节点;如果新vnode节点是含有子节点children,此时如果老节点为text文本节点也好处理,即先让老节点内容置空,然后再插入新节点的children;如果老节点也有很多子节点children,那么此时就是比较复杂的新旧子节点更新策略了,此策略包括4个步骤按序执行命中,即新前旧前、新后旧后、新后旧前、新前旧后,一旦新老子节点命中一个,就停止判断下面的步骤了,然后按照这个策略规定移动指针和插入节点();然后如果指针移动结束后,newVnode还有节点未处理,那么就是需要插入的节点,遍历将这些节点插入到旧dom的oldEndNode之后就可以了;另一方面如果oldvnode还剩余节点未处理,那么就删除oldVnode未处理的节点就可以了。
除了核心功能默认内置的指令 (v-model 、 v-show等),Vue 也允许注册自定义指令。如果你需要对普通 DOM 元素进行底层操作,这时候就会用到它。举个聚焦输入框的例子,如下: