Skip to content

Commit 2ee516d

Browse files
committed
ensure updated hook is called after children are updated as well (fix vuejs#4599)
1 parent 299ecfc commit 2ee516d

File tree

4 files changed

+50
-6
lines changed

4 files changed

+50
-6
lines changed

src/core/instance/lifecycle.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -109,9 +109,8 @@ export function lifecycleMixin (Vue: Class<Component>) {
109109
if (vm.$vnode && vm.$parent && vm.$vnode === vm.$parent._vnode) {
110110
vm.$parent.$el = vm.$el
111111
}
112-
if (vm._isMounted) {
113-
callHook(vm, 'updated')
114-
}
112+
// updated hook is called by the scheduler to ensure that children are
113+
// updated in a parent's updated hook.
115114
}
116115

117116
Vue.prototype._updateFromParent = function (

src/core/observer/scheduler.js

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import type Watcher from './watcher'
44
import config from '../config'
5+
import { callHook } from '../instance/lifecycle'
56
import {
67
warn,
78
nextTick,
@@ -32,6 +33,7 @@ function resetSchedulerState () {
3233
*/
3334
function flushSchedulerQueue () {
3435
flushing = true
36+
let watcher, id, vm
3537

3638
// Sort queue before flush.
3739
// This ensures that:
@@ -46,8 +48,8 @@ function flushSchedulerQueue () {
4648
// do not cache length because more watchers might be pushed
4749
// as we run existing watchers
4850
for (index = 0; index < queue.length; index++) {
49-
const watcher = queue[index]
50-
const id = watcher.id
51+
watcher = queue[index]
52+
id = watcher.id
5153
has[id] = null
5254
watcher.run()
5355
// in dev build, check and stop circular updates.
@@ -67,6 +69,16 @@ function flushSchedulerQueue () {
6769
}
6870
}
6971

72+
// call updated hooks
73+
index = queue.length
74+
while (index--) {
75+
watcher = queue[index]
76+
vm = watcher.vm
77+
if (vm._watcher === watcher && vm._isMounted) {
78+
callHook(vm, 'updated')
79+
}
80+
}
81+
7082
// devtool hook
7183
/* istanbul ignore if */
7284
if (devtools && config.devtools) {

test/unit/features/options/lifecycle.spec.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,34 @@ describe('Options lifecyce hooks', () => {
156156
expect(spy).toHaveBeenCalled()
157157
}).then(done)
158158
})
159+
160+
it('should be called after children are updated', done => {
161+
const calls = []
162+
const vm = new Vue({
163+
template: '<div><test ref="child">{{ msg }}</test></div>',
164+
data: { msg: 'foo' },
165+
components: {
166+
test: {
167+
template: `<div><slot></slot></div>`,
168+
updated () {
169+
expect(this.$el.textContent).toBe('bar')
170+
calls.push('child')
171+
}
172+
}
173+
},
174+
updated () {
175+
expect(this.$el.textContent).toBe('bar')
176+
calls.push('parent')
177+
}
178+
}).$mount()
179+
180+
expect(calls).toEqual([])
181+
vm.msg = 'bar'
182+
expect(calls).toEqual([])
183+
waitForUpdate(() => {
184+
expect(calls).toEqual(['child', 'parent'])
185+
}).then(done)
186+
})
159187
})
160188

161189
describe('beforeDestroy', () => {

test/unit/modules/observer/scheduler.spec.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
import Vue from 'vue'
22
import config from 'core/config'
3-
import { queueWatcher } from 'core/observer/scheduler'
3+
import { queueWatcher as _queueWatcher } from 'core/observer/scheduler'
4+
5+
function queueWatcher (watcher) {
6+
watcher.vm = {} // mock vm
7+
_queueWatcher(watcher)
8+
}
49

510
describe('Scheduler', () => {
611
let spy

0 commit comments

Comments
 (0)