Explorar el Código

handle static vnodes properly during patch (fix #3325)

Evan You hace 10 años
padre
commit
74f8b98509

+ 7 - 4
src/core/instance/render.js

@@ -103,13 +103,16 @@ export function renderMixin (Vue: Class<Component>) {
   // number conversion
   Vue.prototype._n = toNumber
 
-  //
+  // render static tree by index
   Vue.prototype._m = function renderStatic (index?: number): Object | void {
-    return this._staticTrees[index] || (
-      this._staticTrees[index] = this.$options.staticRenderFns[index].call(
+    let tree = this._staticTrees[index]
+    if (!tree) {
+      tree = this._staticTrees[index] = this.$options.staticRenderFns[index].call(
         this._renderProxy
       )
-    )
+      tree.isStatic = true
+    }
+    return tree
   }
 
   // filter resolution helper

+ 9 - 2
src/core/vdom/patch.js

@@ -24,6 +24,9 @@ function isDef (s) {
 }
 
 function sameVnode (vnode1, vnode2) {
+  if (vnode1.isStatic || vnode2.isStatic) {
+    return vnode1 === vnode2
+  }
   return (
     vnode1.key === vnode2.key &&
     vnode1.tag === vnode2.tag &&
@@ -259,8 +262,12 @@ export function createPatchFunction (backend) {
         newStartVnode = newCh[++newStartIdx]
       } else {
         if (isUndef(oldKeyToIdx)) oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx)
-        idxInOld = oldKeyToIdx[newStartVnode.key]
-        if (isUndef(idxInOld)) { // New element
+        idxInOld = isDef(newStartVnode.key)
+          ? oldKeyToIdx[newStartVnode.key]
+          : newStartVnode.isStatic
+            ? oldCh.indexOf(newStartVnode)
+            : null
+        if (isUndef(idxInOld) || idxInOld === -1) { // New element
           nodeOps.insertBefore(parentElm, createElm(newStartVnode, insertedVnodeQueue), oldStartVnode.elm)
           newStartVnode = newCh[++newStartIdx]
         } else {

+ 2 - 0
src/core/vdom/vnode.js

@@ -14,6 +14,7 @@ export default class VNode {
   child: Component | void;
   parent: VNode | void;
   raw: ?boolean;
+  isStatic: ?boolean;
 
   constructor (
     tag?: string,
@@ -39,6 +40,7 @@ export default class VNode {
     this.child = undefined
     this.parent = undefined
     this.raw = false
+    this.isStatic = false
     // apply construct hook.
     // this is applied during render, before patch happens.
     // unlike other hooks, this is applied on both client and server.

+ 20 - 0
test/unit/modules/vdom/patch/children.spec.js

@@ -466,4 +466,24 @@ describe('children', () => {
     expect(child1).not.toBe(child2)
     expect(child2.className).toBe('')
   })
+
+  it('should handle static vnodes properly', function () {
+    function makeNode (text) {
+      return new VNode('div', undefined, [
+        new VNode(undefined, undefined, undefined, text)
+      ])
+    }
+    const b = makeNode('B')
+    b.isStatic = true
+    const vnode1 = new VNode('div', {}, [makeNode('A'), b, makeNode('C')])
+    const vnode2 = new VNode('div', {}, [b])
+    const vnode3 = new VNode('div', {}, [makeNode('A'), b, makeNode('C')])
+
+    let elm = patch(vnode0, vnode1)
+    expect(elm.textContent).toBe('ABC')
+    elm = patch(vnode1, vnode2)
+    expect(elm.textContent).toBe('B')
+    elm = patch(vnode2, vnode3)
+    expect(elm.textContent).toBe('ABC')
+  })
 })