fix activate hook call timing (fix vue-router#1212)

This commit is contained in:
Evan You 2017-04-07 15:30:37 +08:00
parent 931aaea8dc
commit 1073035164
4 changed files with 62 additions and 23 deletions

View File

@ -9,6 +9,7 @@ declare type VNodeComponentOptions = {
}
declare type MountedComponentVNode = {
context: Component;
componentOptions: VNodeComponentOptions;
componentInstance: Component;
parent: VNode;

View File

@ -270,7 +270,7 @@ export function activateChildComponent (vm: Component, direct?: boolean) {
} else if (vm._directInactive) {
return
}
if (vm._inactive || vm._inactive == null) {
if (vm._inactive || vm._inactive === null) {
vm._inactive = false
for (let i = 0; i < vm.$children.length; i++) {
activateChildComponent(vm.$children[i])

View File

@ -2,7 +2,7 @@
import type Watcher from './watcher'
import config from '../config'
import { callHook } from '../instance/lifecycle'
import { callHook, activateChildComponent } from '../instance/lifecycle'
import {
warn,
@ -13,6 +13,7 @@ import {
export const MAX_UPDATE_COUNT = 100
const queue: Array<Watcher> = []
const activatedChildren: Array<Component> = []
let has: { [key: number]: ?true } = {}
let circular: { [key: number]: number } = {}
let waiting = false
@ -23,7 +24,7 @@ let index = 0
* Reset the scheduler's state.
*/
function resetSchedulerState () {
queue.length = 0
queue.length = activatedChildren.length = 0
has = {}
if (process.env.NODE_ENV !== 'production') {
circular = {}
@ -36,7 +37,7 @@ function resetSchedulerState () {
*/
function flushSchedulerQueue () {
flushing = true
let watcher, id, vm
let watcher, id
// Sort queue before flush.
// This ensures that:
@ -72,19 +73,15 @@ function flushSchedulerQueue () {
}
}
// reset scheduler before updated hook called
const oldQueue = queue.slice()
// keep copies of post queues before resetting state
const activatedQueue = activatedChildren.slice()
const updatedQueue = queue.slice()
resetSchedulerState()
// call updated hooks
index = oldQueue.length
while (index--) {
watcher = oldQueue[index]
vm = watcher.vm
if (vm._watcher === watcher && vm._isMounted) {
callHook(vm, 'updated')
}
}
// call component updated and activated hooks
callActivatedHooks(activatedQueue)
callUpdateHooks(updatedQueue)
// devtool hook
/* istanbul ignore if */
@ -93,6 +90,35 @@ function flushSchedulerQueue () {
}
}
function callUpdateHooks (queue) {
let i = queue.length
while (i--) {
const watcher = queue[i]
const vm = watcher.vm
if (vm._watcher === watcher && vm._isMounted) {
callHook(vm, 'updated')
}
}
}
/**
* Queue a kept-alive component that was activated during patch.
* The queue will be processed after the entire tree has been patched.
*/
export function queueActivatedComponent (vm: Component) {
// setting _inactive to false here so that a render function can
// rely on checking whether it's in an inactive tree (e.g. router-view)
vm._inactive = false
activatedChildren.push(vm)
}
function callActivatedHooks (queue) {
for (let i = 0; i < queue.length; i++) {
queue[i]._inactive = true
activateChildComponent(queue[i], true /* true */)
}
}
/**
* Push a watcher into the watcher queue.
* Jobs with duplicate IDs will be skipped unless it's

View File

@ -1,7 +1,8 @@
/* @flow */
import VNode from './vnode'
import { resolveConstructorOptions } from '../instance/init'
import { resolveConstructorOptions } from 'core/instance/init'
import { queueActivatedComponent } from 'core/observer/scheduler'
import { createFunctionalComponent } from './create-functional-component'
import {
@ -61,21 +62,32 @@ const componentVNodeHooks = {
},
insert (vnode: MountedComponentVNode) {
if (!vnode.componentInstance._isMounted) {
vnode.componentInstance._isMounted = true
callHook(vnode.componentInstance, 'mounted')
const { context, componentInstance } = vnode
if (!componentInstance._isMounted) {
componentInstance._isMounted = true
callHook(componentInstance, 'mounted')
}
if (vnode.data.keepAlive) {
activateChildComponent(vnode.componentInstance, true /* direct */)
if (context._isMounted) {
// vue-router#1212
// During updates, a kept-alive component's child components may
// change, so directly walking the tree here may call activated hooks
// on incorrect children. Instead we push them into a queue which will
// be processed after the whole patch process ended.
queueActivatedComponent(componentInstance)
} else {
activateChildComponent(componentInstance, true /* direct */)
}
}
},
destroy (vnode: MountedComponentVNode) {
if (!vnode.componentInstance._isDestroyed) {
const { componentInstance } = vnode
if (!componentInstance._isDestroyed) {
if (!vnode.data.keepAlive) {
vnode.componentInstance.$destroy()
componentInstance.$destroy()
} else {
deactivateChildComponent(vnode.componentInstance, true /* direct */)
deactivateChildComponent(componentInstance, true /* direct */)
}
}
}