Skip to content

Commit fce3f04

Browse files
HerringtonDarkholmeyyx990803
authored andcommitted
Ensure Vue instance's vnode and element is up to date (vuejs#4299)
* fix vuejs#4284, recursively update vnode element * fix vuejs#4284, ensure vm's vnode is up to date * add test for edge case
1 parent 9d0bc97 commit fce3f04

File tree

3 files changed

+67
-2
lines changed

3 files changed

+67
-2
lines changed

src/core/instance/lifecycle.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,10 @@ export function lifecycleMixin (Vue: Class<Component>) {
116116
const vm: Component = this
117117
const hasChildren = !!(vm.$options._renderChildren || renderChildren)
118118
vm.$options._parentVnode = parentVnode
119+
vm.$vnode = parentVnode // update vm's placeholder node without re-render
120+
if (vm._vnode) { // update child tree's parent
121+
vm._vnode.parent = parentVnode
122+
}
119123
vm.$options._renderChildren = renderChildren
120124
// update props
121125
if (propsData && vm.$options.props) {

src/core/vdom/patch.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -505,9 +505,13 @@ export function createPatchFunction (backend) {
505505
createElm(vnode, insertedVnodeQueue)
506506

507507
// component root element replaced.
508-
// update parent placeholder node element.
508+
// update parent placeholder node element, recursively
509509
if (vnode.parent) {
510-
vnode.parent.elm = vnode.elm
510+
let ancestor = vnode.parent
511+
while (ancestor) {
512+
ancestor.elm = vnode.elm
513+
ancestor = ancestor.parent
514+
}
511515
if (isPatchable(vnode)) {
512516
for (let i = 0; i < cbs.create.length; ++i) {
513517
cbs.create[i](emptyNode, vnode.parent)

test/unit/modules/vdom/patch/edge-cases.spec.js

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,4 +57,61 @@ describe('vdom patch: edge cases', () => {
5757
expect(vm.$el.querySelector('.d').textContent).toBe('2')
5858
}).then(done)
5959
})
60+
61+
it('should synchronize vm\' vnode', done => {
62+
const comp = {
63+
data: () => ({ swap: true }),
64+
render (h) {
65+
return this.swap
66+
? h('a', 'atag')
67+
: h('span', 'span')
68+
}
69+
}
70+
71+
const wrapper = {
72+
render: h => h('comp'),
73+
components: { comp }
74+
}
75+
76+
const vm = new Vue({
77+
render (h) {
78+
const children = [
79+
h('wrapper'),
80+
h('div', 'row')
81+
]
82+
if (this.swap) {
83+
children.reverse()
84+
}
85+
return h('div', children)
86+
},
87+
data: () => ({ swap: false }),
88+
components: { wrapper }
89+
}).$mount()
90+
91+
expect(vm.$el.innerHTML).toBe('<a>atag</a><div>row</div>')
92+
const wrapperVm = vm.$children[0]
93+
const compVm = wrapperVm.$children[0]
94+
vm.swap = true
95+
waitForUpdate(() => {
96+
expect(compVm.$vnode.parent).toBe(wrapperVm.$vnode)
97+
expect(vm.$el.innerHTML).toBe('<div>row</div><a>atag</a>')
98+
vm.swap = false
99+
})
100+
.then(() => {
101+
expect(compVm.$vnode.parent).toBe(wrapperVm.$vnode)
102+
expect(vm.$el.innerHTML).toBe('<a>atag</a><div>row</div>')
103+
compVm.swap = false
104+
})
105+
.then(() => {
106+
expect(vm.$el.innerHTML).toBe('<span>span</span><div>row</div>')
107+
expect(compVm.$vnode.parent).toBe(wrapperVm.$vnode)
108+
vm.swap = true
109+
})
110+
.then(() => {
111+
expect(vm.$el.innerHTML).toBe('<div>row</div><span>span</span>')
112+
expect(compVm.$vnode.parent).toBe(wrapperVm.$vnode)
113+
vm.swap = true
114+
})
115+
.then(done)
116+
})
60117
})

0 commit comments

Comments
 (0)