Przeglądaj źródła

fix patch modules error on empty component root

Evan You 9 lat temu
rodzic
commit
efb603570e

+ 4 - 3
src/core/instance/lifecycle.js

@@ -81,15 +81,16 @@ export function lifecycleMixin (Vue: Class<Component>) {
     const prevEl = vm.$el
     const prevActiveInstance = activeInstance
     activeInstance = vm
-    if (!vm._vnode) {
+    const prevVnode = vm._vnode
+    vm._vnode = vnode
+    if (!prevVnode) {
       // Vue.prototype.__patch__ is injected in entry points
       // based on the rendering backend used.
       vm.$el = vm.__patch__(vm.$el, vnode, hydrating)
     } else {
-      vm.$el = vm.__patch__(vm._vnode, vnode)
+      vm.$el = vm.__patch__(prevVnode, vnode)
     }
     activeInstance = prevActiveInstance
-    vm._vnode = vnode
     // update __vue__ reference
     if (prevEl) {
       prevEl.__vue__ = null

+ 16 - 5
src/core/vdom/patch.js

@@ -129,6 +129,13 @@ export function createPatchFunction (backend) {
     return vnode.elm
   }
 
+  function isPatchable (vnode) {
+    while (vnode.child) {
+      vnode = vnode.child._vnode
+    }
+    return isDef(vnode.tag)
+  }
+
   function invokeCreateHooks (vnode, insertedVnodeQueue) {
     for (let i = 0; i < cbs.create.length; ++i) {
       cbs.create[i](emptyNode, vnode)
@@ -145,9 +152,11 @@ export function createPatchFunction (backend) {
       insertedVnodeQueue.push.apply(insertedVnodeQueue, vnode.data.pendingInsert)
     }
     vnode.elm = vnode.child.$el
-    invokeCreateHooks(vnode, insertedVnodeQueue)
-    if (vnode.child._vnode.tag) {
+    if (isPatchable(vnode)) {
+      invokeCreateHooks(vnode, insertedVnodeQueue)
       setScope(vnode)
+    } else {
+      insertedVnodeQueue.push(vnode)
     }
   }
 
@@ -322,7 +331,7 @@ export function createPatchFunction (backend) {
     const elm = vnode.elm = oldVnode.elm
     const oldCh = oldVnode.children
     const ch = vnode.children
-    if (hasData) {
+    if (hasData && isPatchable(vnode)) {
       for (i = 0; i < cbs.update.length; ++i) cbs.update[i](oldVnode, vnode)
       if (isDef(hook) && isDef(i = hook.update)) i(oldVnode, vnode)
     }
@@ -467,8 +476,10 @@ export function createPatchFunction (backend) {
         // update parent placeholder node element.
         if (vnode.parent) {
           vnode.parent.elm = vnode.elm
-          for (let i = 0; i < cbs.create.length; ++i) {
-            cbs.create[i](emptyNode, vnode.parent)
+          if (isPatchable(vnode)) {
+            for (let i = 0; i < cbs.create.length; ++i) {
+              cbs.create[i](emptyNode, vnode.parent)
+            }
           }
         }
 

+ 27 - 0
test/unit/features/directives/if.spec.js

@@ -144,4 +144,31 @@ describe('Directive v-if', () => {
       expect(vm.$el.innerHTML.trim()).toBe('<span>bye</span><span>hello</span>')
     }).then(done)
   })
+
+  it('should work properly on component root', done => {
+    const vm = new Vue({
+      template: `
+        <div>
+          <test class="test"></test>
+        </div>
+      `,
+      components: {
+        test: {
+          data () {
+            return { ok: true }
+          },
+          template: '<div v-if="ok" id="ok" class="inner">test</div>'
+        }
+      }
+    }).$mount()
+    expect(vm.$el.innerHTML).toBe('<div id="ok" class="inner test">test</div>')
+    vm.$children[0].ok = false
+    waitForUpdate(() => {
+      // attrs / class modules should not attempt to patch the comment node
+      expect(vm.$el.innerHTML).toBe('<!---->')
+      vm.$children[0].ok = true
+    }).then(() => {
+      expect(vm.$el.innerHTML).toBe('<div id="ok" class="inner test">test</div>')
+    }).then(done)
+  })
 })

+ 0 - 1
test/unit/features/options/directives.spec.js

@@ -33,7 +33,6 @@ describe('Options directives', () => {
             updateSpy()
             assertContext(el, binding, vnode)
             expect(el).toBe(vm.$el)
-            expect(oldVnode).toBe(vm._vnode)
             expect(oldVnode).not.toBe(vnode)
             expect(binding.expression).toBe('a')
             if (binding.value !== binding.oldValue) {

+ 1 - 1
test/unit/features/transition/transition.spec.js

@@ -558,7 +558,7 @@ if (!isIE9) {
       }).then(done)
     })
 
-    fit('transition with v-show, with transition outside and v-show inside', done => {
+    it('transition with v-show, with transition outside and v-show inside', done => {
       const vm = new Vue({
         template: `
           <div>