美文网首页程序员
【通俗易懂】虚拟DOM,如何更高效DIFF

【通俗易懂】虚拟DOM,如何更高效DIFF

作者: 蛋先生DX | 来源:发表于2019-02-19 15:43 被阅读5次
diff

我们都知道,通过虚拟DOM,可以减少DOM操作,提高页面渲染性能

要实现虚拟DOM,主要三部曲:

  • compile view to vnode
  • diff vnode 的变化
  • patch vnode 变化到实际的DOM

假想的黑粉:"所以这篇文章是要深入虚拟DOM的实现原理和实现细节吗?"

非也非也,我们开始愉快地进入正题吧


三部曲中,diff的性能很关键,所以一般对vnode的typekey作比较,如果不一致,则该vnode及以下孩子们全部干掉(好残忍,无法直视(>﹏<)),用新的直接替换,不再往下对比。

假想的黑粉:“这个大家都懂,所以文章至此可以结束了?”

还没。。。还没开始(# ̄▽ ̄#)

diff,那就得有diff的两个vnode,一个old vnode,一个new vnode,那new vnode如何产生的呢?

假想的黑粉:“简单啊,把old vnode赋值给new vnode”

额~,像以下这样吗?

let oldVnode = {a: {a1: 1}, b: {b1: 1}}
let newVnode = oldVnode
newVnode === oldVnode // true

可以看到新旧一致,无论如何赋值都是同个对象,无从对比啦

假想的黑粉:“我是想说clone一个啦,shadow就行了” <( ̄︶ ̄)>

额~,像以下这样吗?

let oldVnode = {a: {a1: 1}, b: {b1: 1}}
let newVnode = Object.assign({}, oldVnode)
oldVnode === newVnode // false
newVnode.a.a1 = 2
oldVnode.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 = 2
oldVnode === newVnode // false
oldVnode.a.a1 === newVnode.a.a1 // false

看上去没什么问题,功能是可以实现了,但这篇文章是要讲 高效diff,上面方案有两个较不好的性能问题:

  1. deep copy造成资源浪费,没更新的结点也被复制了一份
  2. 每次要遍历所有vnode进行对比,无论该vnode有没产生变化

假想的黑粉:“看样子你是有更好的方案,有什么花招赶紧使出来吧~”

那就让我慢慢道来,先来个中横线分割一下先( ̄︶ ̄)↗


只要避免上面提到的两点对性能的影响,即可更高效DIFF,对应的措施如下:

  1. 按需copy:没出现变化的vnode不作copy
  2. 按需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);

相关文章

  • 【通俗易懂】虚拟DOM,如何更高效DIFF

    我们都知道,通过虚拟DOM,可以减少DOM操作,提高页面渲染性能 要实现虚拟DOM,主要三部曲: compile ...

  • 虚拟dom和diff算法

    虚拟DOM和diff算法 diff:精细化比对最小量更新 真实DOM和虚拟DOM 虚拟DOM:用JavaScrip...

  • React进阶篇(三)diff算法

    如何计算Virtual Dom中真正变化的部分,这就需要diff算法。 Virtual Dom配合高效的diff算...

  • React虚拟DOM分析

    1.简介 虚拟DOM主要包括 Virtual DOM模型 生命周期的维护和管理 性能高效的diff算法 将虚拟DO...

  • Diff 算法、key

    概念 DOM diff 就是对比两棵虚拟 DOM 树的算法。当组件变化时,会得到一个新的虚拟 DOM,diff 算...

  • react VS Vue diff算法

    react diff diff算法的作用:数据更改,生成相应的虚拟DOM,与真实DOM作对比,通过diff算法,对...

  • 浅谈React的diff算法和key属性

    React快速的致胜法宝是虚拟DOM及其高效的diff算法。 可以无需担心性能问题而“随时”刷新整个页面,虚拟DO...

  • 深入理解react中的虚拟DOM、diff算法

    文章结构: React中的虚拟DOM是什么? 虚拟DOM的简单实现(diff算法) 虚拟DOM的内部工作原理 Re...

  • 第十七天

    1.你怎么理解vue中的diff算法? diff算法是虚拟DOM技术的必然产物:通过新旧虚拟DOM作对比(即dif...

  • vue系列---vue-diff

    1.vue-diff 是什么? 提到vue的diff算法就不得不提一个名词 虚拟dom(Virtual DOM) ...

网友评论

    本文标题:【通俗易懂】虚拟DOM,如何更高效DIFF

    本文链接:https://www.haomeiwen.com/subject/zvcxyqtx.html