vue 虚拟dom的diff分析

        引用资料:https://segmentfault.com/a/1190000008782928

        最早是react有虚拟dom,效率相比直接操作dom结构提高了N倍,这两天看了下vue的虚拟dom,现在和大家一起分享,大家如果想全部看,可以看看上面的引用资料,看得仔细的话加上自己的思维去思考,应该是可以明白diff当中的奥义的。

     这里我只分享diff的算法,个人理解,欢迎交流

    几个要看懂的函数:

        1:patch

        2:patchVnode

        3:updateChildren【核心,真正对比新旧两个数组的算法】

```

updateChildren (parentElm, oldCh, newCh) {

    let oldStartIdx = 0, newStartIdx = 0

    let oldEndIdx = oldCh.length - 1

    let oldStartVnode = oldCh[0]

    let oldEndVnode = oldCh[oldEndIdx]

    let newEndIdx = newCh.length - 1

    let newStartVnode = newCh[0]

    let newEndVnode = newCh[newEndIdx]

    let oldKeyToIdx

    let idxInOld

    let elmToMove

    let before

    while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {

            if (oldStartVnode == null) {  //对于vnode.key的比较,会把oldVnode = null

                oldStartVnode = oldCh[++oldStartIdx]

            }else if (oldEndVnode == null) {

                oldEndVnode = oldCh[--oldEndIdx]

            }else if (newStartVnode == null) {

                newStartVnode = newCh[++newStartIdx]

            }else if (newEndVnode == null) {

                newEndVnode = newCh[--newEndIdx]

            }else if (sameVnode(oldStartVnode, newStartVnode)) {

                patchVnode(oldStartVnode, newStartVnode)

                oldStartVnode = oldCh[++oldStartIdx]

                newStartVnode = newCh[++newStartIdx]

            }else if (sameVnode(oldEndVnode, newEndVnode)) {

                patchVnode(oldEndVnode, newEndVnode)

                oldEndVnode = oldCh[--oldEndIdx]

                newEndVnode = newCh[--newEndIdx]

            }else if (sameVnode(oldStartVnode, newEndVnode)) {

                patchVnode(oldStartVnode, newEndVnode)

                api.insertBefore(parentElm, oldStartVnode.el, api.nextSibling(oldEndVnode.el))

                oldStartVnode = oldCh[++oldStartIdx]

                newEndVnode = newCh[--newEndIdx]

            }else if (sameVnode(oldEndVnode, newStartVnode)) {

                patchVnode(oldEndVnode, newStartVnode)

                api.insertBefore(parentElm, oldEndVnode.el, oldStartVnode.el)

                oldEndVnode = oldCh[--oldEndIdx]

                newStartVnode = newCh[++newStartIdx]

            }else {

              // 使用key时的比较

                if (oldKeyToIdx === undefined) {

                    oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx) // 有key生成index表

                }

                idxInOld = oldKeyToIdx[newStartVnode.key]

                if (!idxInOld) {

                    api.insertBefore(parentElm, createEle(newStartVnode).el, oldStartVnode.el)

                    newStartVnode = newCh[++newStartIdx]

                }

                else {

                    elmToMove = oldCh[idxInOld]

                    if (elmToMove.sel !== newStartVnode.sel) {

                        api.insertBefore(parentElm, createEle(newStartVnode).el, oldStartVnode.el)

                    }else {

                        patchVnode(elmToMove, newStartVnode)

                        oldCh[idxInOld] = null

                        api.insertBefore(parentElm, elmToMove.el, oldStartVnode.el)

                    }

                    newStartVnode = newCh[++newStartIdx]

                }

            }

        }

        if (oldStartIdx > oldEndIdx) {

            before = newCh[newEndIdx + 1] == null ? null : newCh[newEndIdx + 1].el

            addVnodes(parentElm, before, newCh, newStartIdx, newEndIdx)

        }else if (newStartIdx > newEndIdx) {

            removeVnodes(parentElm, oldCh, oldStartIdx, oldEndIdx)

        }

}

```

        这里的代码其实是在匹配操作新旧两个数组,如果旧数组有,新数组没有的话,最终会移除,新数组有,旧数组没有则会被插入,只是插入的位置是新数组匹配时候旧数组index的前一位,这里几幅图解释下如何匹配的更新插入删除操作的。【别人讲的都是两个数组,四个变量,相互往中间推,任何一个先操作完就算结束】

    

新旧数组匹配示意图

    过程可以概括为:oldCh和newCh各有两个头尾的变量StartIdx和EndIdx,它们的2个变量相互比较,一共有4种比较方式。如果4种比较都没匹配,如果设置了key,就会用key进行比较,在比较的过程中,变量会往中间靠,一旦StartIdx>EndIdx表明oldCh和newCh至少有一个已经遍历完了,就会结束比较

但是我觉得这里没有讲具体怎么靠。

newStartIndex会在oldCh内部循环,从oldStartIndex到oldEndIndex,无论在oldCh内部是否找到有相同的节点,newStartIndex都会往右偏移,

    这里分两种情况,匹配上和没匹配上

    匹配上:直接插入到oldStartIndex节点前面,newStartIndex++,开始下一轮循环,同样从oldStartIndex到oldEndIndex.

    没匹配上:直接插入到oldStartIndex节点前面,循环到oldEndIndex,newStartIndex++,开始下一轮循环,同样从oldStartIndex到oldEndIndex.

      oldStartIndex++,oldEndIndex--,和newEndIndex--,会在什么情况下出现,只能出现oldStartIndex节点==newEndIndex节点或者newEndIndex节点==oldEndIndex节点或者oldEndIndex节点==newStartIndex节点,这样情况才会出现数组除了newStartIndex节点往内靠,其他节点也往内靠

    结束情况,代码当中,

        如果oldStartIdx > oldEndIdx,说明就数组先遍历完,新数组没有,说明新数组有多,然后需要将新数组新的给添加进去

     如果newStartIdx > newEndIdx,说明新数组先遍历完,旧数组没有,说明新数组变少了,旧数组有多余,得移除掉旧数组多余的。

    核心,得记住,遍历每次一直往中间靠的是newStartIndex节点,而不是所有节点


    if (oldStartIdx > oldEndIdx) {

            before = newCh[newEndIdx + 1] == null ? null : newCh[newEndIdx + 1].el

            addVnodes(parentElm, before, newCh, newStartIdx, newEndIdx)

        }else if (newStartIdx > newEndIdx) {

            removeVnodes(parentElm, oldCh, oldStartIdx, oldEndIdx)

        }

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,734评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,931评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,133评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,532评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,585评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,462评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,262评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,153评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,587评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,792评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,919评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,635评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,237评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,855评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,983评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,048评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,864评论 2 354