【浅显易懂】虚拟DOM,如何更高效DIFF
diff
我们都知道,通过虚拟DOM,可以减少DOM操作,提高页面渲染性能
要实现虚拟DOM,主要三部曲:
- compile view to vnode
- diff vnode 的变化
- patch vnode 变化到实际的DOM
假想的黑粉:”所以这篇文章是要深入虚拟DOM的实现原理和实现细节吗?”
非也非也,我们开始愉快地进入正题吧
三部曲中,diff的性能很关键,所以一般对vnode的type和key作比较,假如不一致,则该vnode及以下孩子们一律干掉(好残忍,无法直视(>﹏<)),用新的直接替换,不再往下比照。
假想的黑粉:“这个大家都懂,所以文章至此可以结束了?”
还没。。。还没开始(# ̄▽ ̄#)
要diff,那就得有diff的两个vnode,一个old vnode,一个new vnode,那new vnode如何产生的呢?
假想的黑粉:“简单啊,把old vnode赋值给new vnode”
额~,像以下这样吗?
let oldVnode = {a: {a1: 1}, b: {b1: 1}}let newVnode = oldVnodenewVnode === oldVnode // true可以看到新旧一致,无论如何赋值都是同个对象,无从比照啦
假想的黑粉:“我是想说clone一个啦,shadow就行了” <( ̄︶ ̄)>
额~,像以下这样吗?
let oldVnode = {a: {a1: 1}, b: {b1: 1}}let newVnode = Object.assign({}, oldVnode)oldVnode === newVnode // falsenewVnode.a.a1 = 2oldVnode.a.a1 // 2可以看到更改了new vnode的a1值,old vnode的a1值也被改了,也就无法得知变化了
假想的黑粉:“刚为了性能考虑说了shadow copy,那实在不行就deep copy吧”
额~,像以下这样吗?
const _ = require('lodash');let oldVnode = {a: {a1: 1}, b: {b1: 1}}let newVnode = _.cloneDeep(oldVnode)newVnode.a.a1 = 2oldVnode === newVnode // falseoldVnode.a.a1 === newVnode.a.a1 // false看上去没什么问题,功能是可以实现了,但这篇文章是要讲 更 高效diff,上面方案有两个较不好的性能问题:
- deep copy造成资源白费,没升级的结点也被复制了一份
- 每次要遍历所有vnode进行比照,无论该vnode有没产生变化
假想的黑粉:“看样子你是有更好的方案,有什么花招赶紧使出来吧~”
那就让我慢慢道来,先来个中横线分割一下先( ̄︶ ̄)↗
只需避免上面提到的两点对性能的影响,就可更高效DIFF,对应的措施如下:
- 按需copy:没出现变化的vnode不作copy
- 按需diff:没出现变化的vnode不作diff
假设vnode的数据结构以及图形表示如下:
let oldVnode = { a: { a1: { a1_1: 1 }, a2: { a2_1: 1 } }, b: { b1: { b1_1: 1 } }}
image.png
当把 a1_1 的值更改为 2 时, 我们希望只对以下高亮节点进行shadow copy或者赋值,以下即为new vnode
image.png
所以在比照old vnode和new vnode时,只有下图高亮的节点需要进行比对
image.png
当 a2 和 b 节点下面的子节点越多时,copy 和 diff 所带来的性能收益就越显著
最后献上这种方案的极简单极粗糙的实现(update方法,只考虑对象,没考虑数组)以更好的从代码层面去了解这种思路
const assert = require('assert');let oldVal = {a: {a1: 1}, b: {b1: 2}}function update(obj, path, val) { let fileds = path.split('.'); let shadowCopy = targetObj => Object.assign({}, targetObj); let result = shadowCopy(obj); if (fileds.length > 0) { if (fileds.length === 1) { result[fileds[0]] = val; } else { result[fileds[0]] = update(obj[fileds[0]], fileds.length > 1 ? fileds.splice(1).join('.') : '', val) } } return result;}const newVal = update(oldVal, 'a.a1', 2);assert.notStrictEqual(oldVal, newVal);assert.notStrictEqual(oldVal.a, newVal.a);assert.notStrictEqual(oldVal.a.a1, newVal.a.a1);assert.strictEqual(oldVal.b, newVal.b);1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » 【浅显易懂】虚拟DOM,如何更高效DIFF