Просмотр исходного кода

fix(vdom): avoid diff de-opt when both head/tail are different

fix #6502
Evan You 8 лет назад
Родитель
Сommit
230c6ae782
2 измененных файлов с 34 добавлено и 4 удалено
  1. 10 1
      src/core/vdom/patch.js
  2. 24 3
      test/unit/modules/vdom/patch/children.spec.js

+ 10 - 1
src/core/vdom/patch.js

@@ -401,7 +401,9 @@ export function createPatchFunction (backend) {
         newStartVnode = newCh[++newStartIdx]
       } else {
         if (isUndef(oldKeyToIdx)) oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx)
-        idxInOld = isDef(newStartVnode.key) ? oldKeyToIdx[newStartVnode.key] : null
+        idxInOld = isDef(newStartVnode.key)
+          ? oldKeyToIdx[newStartVnode.key]
+          : findIdxInOld(newStartVnode, oldCh, oldStartIdx, oldEndIdx)
         if (isUndef(idxInOld)) { // New element
           createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm)
           newStartVnode = newCh[++newStartIdx]
@@ -435,6 +437,13 @@ export function createPatchFunction (backend) {
     }
   }
 
+  function findIdxInOld (node, oldCh, start, end) {
+    for (let i = start; i < end; i++) {
+      const c = oldCh[i]
+      if (isDef(c) && sameVnode(node, c)) return i
+    }
+  }
+
   function patchVnode (oldVnode, vnode, insertedVnodeQueue, removeOnly) {
     if (oldVnode === vnode) {
       return

+ 24 - 3
test/unit/modules/vdom/patch/children.spec.js

@@ -1,5 +1,5 @@
 import { patch } from 'web/runtime/patch'
-import VNode from 'core/vdom/vnode'
+import VNode, { createEmptyVNode } from 'core/vdom/vnode'
 
 function prop (name) {
   return obj => { return obj[name] }
@@ -445,7 +445,7 @@ describe('vdom patch: children', () => {
     expect(child2.className).toBe('')
   })
 
-  it('should handle static vnodes properly', function () {
+  it('should handle static vnodes properly', () => {
     function makeNode (text) {
       return new VNode('div', undefined, [
         new VNode(undefined, undefined, undefined, text)
@@ -466,7 +466,7 @@ describe('vdom patch: children', () => {
     expect(elm.textContent).toBe('ABC')
   })
 
-  it('should handle static vnodes inside ', function () {
+  it('should handle static vnodes inside ', () => {
     function makeNode (text) {
       return new VNode('div', undefined, [
         new VNode(undefined, undefined, undefined, text)
@@ -486,4 +486,25 @@ describe('vdom patch: children', () => {
     elm = patch(vnode2, vnode3)
     expect(elm.textContent).toBe('ABC')
   })
+
+  // #6502
+  it('should not de-opt when both head and tail are changed', () => {
+    const vnode1 = new VNode('div', {}, [
+      createEmptyVNode(),
+      new VNode('div'),
+      createEmptyVNode()
+    ])
+    const vnode2 = new VNode('div', {}, [
+      new VNode('p'),
+      new VNode('div'),
+      new VNode('p')
+    ])
+    let root = patch(null, vnode1)
+    const original = root.childNodes[1]
+
+    root = patch(vnode1, vnode2)
+    const postPatch = root.childNodes[1]
+
+    expect(postPatch).toBe(original)
+  })
 })