共计 1463 个字符,预计需要花费 4 分钟才能阅读完成。
20190125
Vue 中的 diff 算法?
概念: diff 算法是一种优化手段,将前后两个模块进行差异对比,修补 (更新) 差异的过程叫做 patch(打补丁)
为什么 vue,react 这些框架中都会有 diff 算法呢? 我们都知道渲染真实 dom 的开销是很大的,这个跟性能优化中的重绘重排意义类似, 回到正题来, 有时候我们修改了页面中的某个数据,如果直接渲染到真实 DOM 中会引起整棵数的重绘重排, 那么我们能不能只让我们修改的数据映射到真实 DOM, 做一个最少化重绘重排呢,说到这里你应该对为什么使用 diff 算法有一个简单的概念了
virtual DOM 和真实 DOM 的区别
一句话概括吧,virtual DOM 是将真实的 DOM 的数据抽取出来,以对象的形式模拟树形结构, diff 算法比较的也是 virtual DOM
代码理解
<div>
<p>JS 每日一题 </p>
</div>
// 转换成 VNode 类似于下面这种
const Vnode = {
tag: ‘div’,
children: [
{tag: ‘p’, text: ‘JS 每日一题 ’}
]
};
diff 是如何比较的?
源码太多了,就不贴了,有兴趣的可以自己看看 https://github.com/vuejs/vue/…
简单的说就是新旧虚拟 dom 的比较,如果有差异就以新的为准,然后再插入的真实的 dom 中,重新渲染
特点
只会做同级比较,不做跨级比较
比较后几种情况
if (oldVnode === vnode),他们的引用一致,可以认为没有变化。
if(oldVnode.text !== null && vnode.text !== null && oldVnode.text !== vnode.text),文本节点的比较,需要修改,则会调用 Node.textContent = vnode.text。
if(oldCh && ch && oldCh !== ch), 两个节点都有子节点,而且它们不一样,这样我们会调用 updateChildren 函数比较子节点,这是 diff 的核心
else if (ch),只有新的节点有子节点,调用 createEle(vnode),vnode.el 已经引用了老的 dom 节点,createEle 函数会在老 dom 节点上添加子节点。
else if (oldCh),新节点没有子节点,老节点有子节点,直接删除老节点。
key 的作用
设置 key 和不设置 key 的区别:不设 key,newCh 和 oldCh 只会进行头尾两端的相互比较,设 key 后,除了头尾两端的比较外,还会从用 key 生成的对象 oldKeyToIdx 中查找匹配的节点,所以为节点设置 key 可以更高效的利用 dom
如我们希望可以在 B 和 C 之间加一个 F,Diff 算法默认执行起来是这样的:
即把 C 更新成 F,D 更新成 C,E 更新成 D,最后再插入 E,是不是很没有效率?
所以我们需要使用 key 来给每个节点做一个唯一标识,Diff 算法就可以正确的识别此节点,找到正确的位置区插入新的节点。
所以一句话,key 的作用主要是为了高效的更新虚拟 DOM。另外 vue 中在使用相同标签名元素的过渡切换时,也会使用到 key 属性,其目的也是为了让 vue 可以区分它们,否则 vue 只会替换其内部属性而不会触发过渡效果
总结
尽量不要跨层级的修改 dom
在开发组件时,保持稳定的 DOM 结构会有助于性能的提升
设置 key 可以让 diff 更高效
关于 JS 每日一题
JS 每日一题可以看成是一个语音答题社区 每天利用碎片时间采用 60 秒内的语音形式来完成当天的考题 群主在次日 0 点推送当天的参考答案
注 绝不仅限于完成当天任务,更多是查漏补缺,学习群内其它同学优秀的答题思路
点击加入答题