Explorar o código

fix HOC root node replace updates

Evan You %!s(int64=10) %!d(string=hai) anos
pai
achega
dffeb1d1c2

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

@@ -85,15 +85,9 @@ export function lifecycleMixin (Vue: Class<Component>) {
     if (vm.$el) {
       vm.$el.__vue__ = vm
     }
-    // update parent vnode element after patch
-    const parentNode = vm.$vnode
-    if (parentNode) {
-      parentNode.elm = vm.$el
-      // update parent $el if the parent is HOC
-      // this is necessary because child is updated after parent
-      if (vm.$parent && parentNode === vm.$parent._vnode) {
-        vm.$parent.$el = vm.$el
-      }
+    // if parent is an HOC, update its $el as well
+    if (vm.$vnode && vm.$parent && vm.$vnode === vm.$parent._vnode) {
+      vm.$parent.$el = vm.$el
     }
     if (vm._isMounted) {
       callHook(vm, 'updated')

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

@@ -83,6 +83,7 @@ export function createPatchFunction (backend) {
       // component also has set the placeholder vnode's elm.
       // in that case we can just return the element and be done.
       if (isDef(i = vnode.child)) {
+        vnode.elm = vnode.child.$el
         invokeCreateHooks(vnode, insertedVnodeQueue)
         setScope(vnode)
         return vnode.elm
@@ -415,6 +416,15 @@ export function createPatchFunction (backend) {
 
         createElm(vnode, insertedVnodeQueue)
 
+        // component root element replaced.
+        // 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 (parent !== null) {
           nodeOps.insertBefore(parent, vnode.elm, nodeOps.nextSibling(elm))
           removeVnodes(parent, [oldVnode], 0, 0)

+ 40 - 0
test/unit/features/component/component.spec.js

@@ -236,4 +236,44 @@ describe('Component', () => {
       })
     }).not.toThrow()
   })
+
+  it('properly update replaced higher-order component root node', done => {
+    const vm = new Vue({
+      data: {
+        color: 'red'
+      },
+      template: '<test id="foo" :class="color"></test>',
+      components: {
+        test: {
+          data () {
+            return { tag: 'div' }
+          },
+          render (h) {
+            return h(this.tag, { class: 'test' }, 'hi')
+          }
+        }
+      }
+    }).$mount()
+
+    expect(vm.$el.tagName).toBe('DIV')
+    expect(vm.$el.id).toBe('foo')
+    expect(vm.$el.className).toBe('test red')
+
+    vm.color = 'green'
+    waitForUpdate(() => {
+      expect(vm.$el.tagName).toBe('DIV')
+      expect(vm.$el.id).toBe('foo')
+      expect(vm.$el.className).toBe('test green')
+      vm.$children[0].tag = 'p'
+    }).then(() => {
+      expect(vm.$el.tagName).toBe('P')
+      expect(vm.$el.id).toBe('foo')
+      expect(vm.$el.className).toBe('test green')
+      vm.color = 'red'
+    }).then(() => {
+      expect(vm.$el.tagName).toBe('P')
+      expect(vm.$el.id).toBe('foo')
+      expect(vm.$el.className).toBe('test red')
+    }).then(done)
+  })
 })