Skip to content

Commit 9ccffe7

Browse files
committed
invoke activate/deactivate recursively + refactor (close vuejs#4242)
1 parent 2f520ba commit 9ccffe7

File tree

6 files changed

+312
-123
lines changed

6 files changed

+312
-123
lines changed

flow/component.js

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,8 @@ declare interface Component {
6060
_data: Object;
6161
_props: Object;
6262
_events: Object;
63-
_inactive: boolean;
63+
_inactive: boolean | null;
64+
_directInactive: boolean;
6465
_isMounted: boolean;
6566
_isDestroyed: boolean;
6667
_isBeingDestroyed: boolean;
@@ -73,12 +74,6 @@ declare interface Component {
7374
_init: Function;
7475
_mount: (el?: Element | void, hydrating?: boolean) => Component;
7576
_update: (vnode: VNode, hydrating?: boolean) => void;
76-
_updateFromParent: (
77-
propsData: ?Object,
78-
listeners: ?{ [key: string]: Function | Array<Function> },
79-
parentVnode: VNode,
80-
renderChildren: ?Array<VNode>
81-
) => void;
8277
// rendering
8378
_render: () => VNode;
8479
__patch__: (a: Element | VNode | void, b: VNode) => any;

src/core/instance/lifecycle.js

Lines changed: 137 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -29,52 +29,14 @@ export function initLifecycle (vm: Component) {
2929
vm.$refs = {}
3030

3131
vm._watcher = null
32-
vm._inactive = false
32+
vm._inactive = null
33+
vm._directInactive = false
3334
vm._isMounted = false
3435
vm._isDestroyed = false
3536
vm._isBeingDestroyed = false
3637
}
3738

3839
export function lifecycleMixin (Vue: Class<Component>) {
39-
Vue.prototype._mount = function (
40-
el?: Element | void,
41-
hydrating?: boolean
42-
): Component {
43-
const vm: Component = this
44-
vm.$el = el
45-
if (!vm.$options.render) {
46-
vm.$options.render = createEmptyVNode
47-
if (process.env.NODE_ENV !== 'production') {
48-
/* istanbul ignore if */
49-
if (vm.$options.template && vm.$options.template.charAt(0) !== '#') {
50-
warn(
51-
'You are using the runtime-only build of Vue where the template ' +
52-
'option is not available. Either pre-compile the templates into ' +
53-
'render functions, or use the compiler-included build.',
54-
vm
55-
)
56-
} else {
57-
warn(
58-
'Failed to mount component: template or render function not defined.',
59-
vm
60-
)
61-
}
62-
}
63-
}
64-
callHook(vm, 'beforeMount')
65-
vm._watcher = new Watcher(vm, function updateComponent () {
66-
vm._update(vm._render(), hydrating)
67-
}, noop)
68-
hydrating = false
69-
// manually mounted instance, call mounted on self
70-
// mounted is called for render-created child components in its inserted hook
71-
if (vm.$vnode == null) {
72-
vm._isMounted = true
73-
callHook(vm, 'mounted')
74-
}
75-
return vm
76-
}
77-
7840
Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) {
7941
const vm: Component = this
8042
if (vm._isMounted) {
@@ -115,62 +77,6 @@ export function lifecycleMixin (Vue: Class<Component>) {
11577
// updated in a parent's updated hook.
11678
}
11779

118-
Vue.prototype._updateFromParent = function (
119-
propsData: ?Object,
120-
listeners: ?Object,
121-
parentVnode: VNode,
122-
renderChildren: ?Array<VNode>
123-
) {
124-
const vm: Component = this
125-
126-
// determine whether component has slot children
127-
// we need to do this before overwriting $options._renderChildren
128-
const hasChildren = !!(
129-
renderChildren || // has new static slots
130-
vm.$options._renderChildren || // has old static slots
131-
parentVnode.data.scopedSlots || // has new scoped slots
132-
vm.$scopedSlots !== emptyObject // has old scoped slots
133-
)
134-
135-
vm.$options._parentVnode = parentVnode
136-
vm.$vnode = parentVnode // update vm's placeholder node without re-render
137-
if (vm._vnode) { // update child tree's parent
138-
vm._vnode.parent = parentVnode
139-
}
140-
vm.$options._renderChildren = renderChildren
141-
142-
// update props
143-
if (propsData && vm.$options.props) {
144-
observerState.shouldConvert = false
145-
if (process.env.NODE_ENV !== 'production') {
146-
observerState.isSettingProps = true
147-
}
148-
const props = vm._props
149-
const propKeys = vm.$options._propKeys || []
150-
for (let i = 0; i < propKeys.length; i++) {
151-
const key = propKeys[i]
152-
props[key] = validateProp(key, vm.$options.props, propsData, vm)
153-
}
154-
observerState.shouldConvert = true
155-
if (process.env.NODE_ENV !== 'production') {
156-
observerState.isSettingProps = false
157-
}
158-
// keep a copy of raw propsData
159-
vm.$options.propsData = propsData
160-
}
161-
// update listeners
162-
if (listeners) {
163-
const oldListeners = vm.$options._parentListeners
164-
vm.$options._parentListeners = listeners
165-
updateComponentListeners(vm, listeners, oldListeners)
166-
}
167-
// resolve slots + force update if has children
168-
if (hasChildren) {
169-
vm.$slots = resolveSlots(renderChildren, parentVnode.context)
170-
vm.$forceUpdate()
171-
}
172-
}
173-
17480
Vue.prototype.$forceUpdate = function () {
17581
const vm: Component = this
17682
if (vm._watcher) {
@@ -217,6 +123,141 @@ export function lifecycleMixin (Vue: Class<Component>) {
217123
}
218124
}
219125

126+
export function mountComponent (
127+
vm: Component,
128+
el: ?Element,
129+
hydrating?: boolean
130+
): Component {
131+
vm.$el = el
132+
if (!vm.$options.render) {
133+
vm.$options.render = createEmptyVNode
134+
if (process.env.NODE_ENV !== 'production') {
135+
/* istanbul ignore if */
136+
if (vm.$options.template && vm.$options.template.charAt(0) !== '#') {
137+
warn(
138+
'You are using the runtime-only build of Vue where the template ' +
139+
'option is not available. Either pre-compile the templates into ' +
140+
'render functions, or use the compiler-included build.',
141+
vm
142+
)
143+
} else {
144+
warn(
145+
'Failed to mount component: template or render function not defined.',
146+
vm
147+
)
148+
}
149+
}
150+
}
151+
callHook(vm, 'beforeMount')
152+
vm._watcher = new Watcher(vm, function updateComponent () {
153+
vm._update(vm._render(), hydrating)
154+
}, noop)
155+
hydrating = false
156+
// manually mounted instance, call mounted on self
157+
// mounted is called for render-created child components in its inserted hook
158+
if (vm.$vnode == null) {
159+
vm._isMounted = true
160+
callHook(vm, 'mounted')
161+
}
162+
return vm
163+
}
164+
165+
export function updateChildComponent (
166+
vm: Component,
167+
propsData: ?Object,
168+
listeners: ?Object,
169+
parentVnode: VNode,
170+
renderChildren: ?Array<VNode>
171+
) {
172+
// determine whether component has slot children
173+
// we need to do this before overwriting $options._renderChildren
174+
const hasChildren = !!(
175+
renderChildren || // has new static slots
176+
vm.$options._renderChildren || // has old static slots
177+
parentVnode.data.scopedSlots || // has new scoped slots
178+
vm.$scopedSlots !== emptyObject // has old scoped slots
179+
)
180+
181+
vm.$options._parentVnode = parentVnode
182+
vm.$vnode = parentVnode // update vm's placeholder node without re-render
183+
if (vm._vnode) { // update child tree's parent
184+
vm._vnode.parent = parentVnode
185+
}
186+
vm.$options._renderChildren = renderChildren
187+
188+
// update props
189+
if (propsData && vm.$options.props) {
190+
observerState.shouldConvert = false
191+
if (process.env.NODE_ENV !== 'production') {
192+
observerState.isSettingProps = true
193+
}
194+
const props = vm._props
195+
const propKeys = vm.$options._propKeys || []
196+
for (let i = 0; i < propKeys.length; i++) {
197+
const key = propKeys[i]
198+
props[key] = validateProp(key, vm.$options.props, propsData, vm)
199+
}
200+
observerState.shouldConvert = true
201+
if (process.env.NODE_ENV !== 'production') {
202+
observerState.isSettingProps = false
203+
}
204+
// keep a copy of raw propsData
205+
vm.$options.propsData = propsData
206+
}
207+
// update listeners
208+
if (listeners) {
209+
const oldListeners = vm.$options._parentListeners
210+
vm.$options._parentListeners = listeners
211+
updateComponentListeners(vm, listeners, oldListeners)
212+
}
213+
// resolve slots + force update if has children
214+
if (hasChildren) {
215+
vm.$slots = resolveSlots(renderChildren, parentVnode.context)
216+
vm.$forceUpdate()
217+
}
218+
}
219+
220+
function isInInactiveTree (vm) {
221+
while (vm && (vm = vm.$parent)) {
222+
if (vm._inactive) return true
223+
}
224+
return false
225+
}
226+
227+
export function activateChildComponent (vm: Component, direct?: boolean) {
228+
if (direct) {
229+
vm._directInactive = false
230+
if (isInInactiveTree(vm)) {
231+
return
232+
}
233+
} else if (vm._directInactive) {
234+
return
235+
}
236+
if (vm._inactive || vm._inactive == null) {
237+
vm._inactive = false
238+
for (let i = 0; i < vm.$children.length; i++) {
239+
activateChildComponent(vm.$children[i])
240+
}
241+
callHook(vm, 'activated')
242+
}
243+
}
244+
245+
export function deactivateChildComponent (vm: Component, direct?: boolean) {
246+
if (direct) {
247+
vm._directInactive = true
248+
if (isInInactiveTree(vm)) {
249+
return
250+
}
251+
}
252+
if (!vm._inactive) {
253+
vm._inactive = true
254+
for (let i = 0; i < vm.$children.length; i++) {
255+
deactivateChildComponent(vm.$children[i])
256+
}
257+
callHook(vm, 'deactivated')
258+
}
259+
}
260+
220261
export function callHook (vm: Component, hook: string) {
221262
const handlers = vm.$options[hook]
222263
if (handlers) {

src/core/vdom/create-component.js

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,25 @@
11
/* @flow */
22

33
import VNode from './vnode'
4+
import { createElement } from './create-element'
45
import { resolveConstructorOptions } from '../instance/init'
5-
import { activeInstance, callHook } from '../instance/lifecycle'
66
import { resolveSlots } from '../instance/render-helpers/resolve-slots'
7-
import { createElement } from './create-element'
8-
import { warn, isObject, hasOwn, hyphenate, validateProp } from '../util/index'
7+
8+
import {
9+
warn,
10+
isObject,
11+
hasOwn,
12+
hyphenate,
13+
validateProp
14+
} from '../util/index'
15+
16+
import {
17+
callHook,
18+
activeInstance,
19+
updateChildComponent,
20+
activateChildComponent,
21+
deactivateChildComponent
22+
} from '../instance/lifecycle'
923

1024
const hooks = { init, prepatch, insert, destroy }
1125
const hooksToMerge = Object.keys(hooks)
@@ -183,7 +197,8 @@ function prepatch (
183197
) {
184198
const options = vnode.componentOptions
185199
const child = vnode.componentInstance = oldVnode.componentInstance
186-
child._updateFromParent(
200+
updateChildComponent(
201+
child,
187202
options.propsData, // updated props
188203
options.listeners, // updated listeners
189204
vnode, // new parent vnode
@@ -197,8 +212,7 @@ function insert (vnode: MountedComponentVNode) {
197212
callHook(vnode.componentInstance, 'mounted')
198213
}
199214
if (vnode.data.keepAlive) {
200-
vnode.componentInstance._inactive = false
201-
callHook(vnode.componentInstance, 'activated')
215+
activateChildComponent(vnode.componentInstance, true /* direct */)
202216
}
203217
}
204218

@@ -207,8 +221,7 @@ function destroy (vnode: MountedComponentVNode) {
207221
if (!vnode.data.keepAlive) {
208222
vnode.componentInstance.$destroy()
209223
} else {
210-
vnode.componentInstance._inactive = true
211-
callHook(vnode.componentInstance, 'deactivated')
224+
deactivateChildComponent(vnode.componentInstance, true /* direct */)
212225
}
213226
}
214227
}

src/entries/web-runtime.js

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,26 @@
22

33
import Vue from 'core/index'
44
import config from 'core/config'
5+
import { patch } from 'web/runtime/patch'
56
import { extend, noop } from 'shared/util'
7+
import { mountComponent } from 'core/instance/lifecycle'
68
import { devtools, inBrowser, isEdge } from 'core/util/index'
7-
import { patch } from 'web/runtime/patch'
89
import platformDirectives from 'web/runtime/directives/index'
910
import platformComponents from 'web/runtime/components/index'
11+
1012
import {
1113
query,
12-
isUnknownElement,
14+
mustUseProp,
1315
isReservedTag,
1416
getTagNamespace,
15-
mustUseProp
17+
isUnknownElement
1618
} from 'web/util/index'
1719

1820
// install platform specific utils
19-
Vue.config.isUnknownElement = isUnknownElement
21+
Vue.config.mustUseProp = mustUseProp
2022
Vue.config.isReservedTag = isReservedTag
2123
Vue.config.getTagNamespace = getTagNamespace
22-
Vue.config.mustUseProp = mustUseProp
24+
Vue.config.isUnknownElement = isUnknownElement
2325

2426
// install platform runtime directives & components
2527
extend(Vue.options.directives, platformDirectives)
@@ -28,13 +30,13 @@ extend(Vue.options.components, platformComponents)
2830
// install platform patch function
2931
Vue.prototype.__patch__ = inBrowser ? patch : noop
3032

31-
// wrap mount
33+
// public mount method
3234
Vue.prototype.$mount = function (
3335
el?: string | Element,
3436
hydrating?: boolean
3537
): Component {
3638
el = el && inBrowser ? query(el) : undefined
37-
return this._mount(el, hydrating)
39+
return mountComponent(this, el, hydrating)
3840
}
3941

4042
// devtools global hook

0 commit comments

Comments
 (0)