1 |
|
2 |
|
3 | import config from '../config'
|
4 | import Watcher from '../observer/watcher'
|
5 | import { mark, measure } from '../util/perf'
|
6 | import { createEmptyVNode } from '../vdom/vnode'
|
7 | import { updateComponentListeners } from './events'
|
8 | import { resolveSlots } from './render-helpers/resolve-slots'
|
9 | import { toggleObserving } from '../observer/index'
|
10 | import { pushTarget, popTarget } from '../observer/dep'
|
11 |
|
12 | import {
|
13 | warn,
|
14 | noop,
|
15 | remove,
|
16 | emptyObject,
|
17 | validateProp,
|
18 | invokeWithErrorHandling
|
19 | } from '../util/index'
|
20 |
|
21 | export let activeInstance: any = null
|
22 | export let isUpdatingChildComponent: boolean = false
|
23 |
|
24 | export function setActiveInstance(vm: Component) {
|
25 | const prevActiveInstance = activeInstance
|
26 | activeInstance = vm
|
27 | return () => {
|
28 | activeInstance = prevActiveInstance
|
29 | }
|
30 | }
|
31 |
|
32 | export function initLifecycle (vm: Component) {
|
33 | const options = vm.$options
|
34 |
|
35 |
|
36 | let parent = options.parent
|
37 | if (parent && !options.abstract) {
|
38 | while (parent.$options.abstract && parent.$parent) {
|
39 | parent = parent.$parent
|
40 | }
|
41 | parent.$children.push(vm)
|
42 | }
|
43 |
|
44 | vm.$parent = parent
|
45 | vm.$root = parent ? parent.$root : vm
|
46 |
|
47 | vm.$children = []
|
48 | vm.$refs = {}
|
49 |
|
50 | vm._watcher = null
|
51 | vm._inactive = null
|
52 | vm._directInactive = false
|
53 | vm._isMounted = false
|
54 | vm._isDestroyed = false
|
55 | vm._isBeingDestroyed = false
|
56 | }
|
57 |
|
58 | export function lifecycleMixin (Vue: Class<Component>) {
|
59 | Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) {
|
60 | const vm: Component = this
|
61 | const prevEl = vm.$el
|
62 | const prevVnode = vm._vnode
|
63 | const restoreActiveInstance = setActiveInstance(vm)
|
64 | vm._vnode = vnode
|
65 |
|
66 |
|
67 | if (!prevVnode) {
|
68 |
|
69 | vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false )
|
70 | } else {
|
71 |
|
72 | vm.$el = vm.__patch__(prevVnode, vnode)
|
73 | }
|
74 | restoreActiveInstance()
|
75 |
|
76 | if (prevEl) {
|
77 | prevEl.__vue__ = null
|
78 | }
|
79 | if (vm.$el) {
|
80 | vm.$el.__vue__ = vm
|
81 | }
|
82 |
|
83 | if (vm.$vnode && vm.$parent && vm.$vnode === vm.$parent._vnode) {
|
84 | vm.$parent.$el = vm.$el
|
85 | }
|
86 |
|
87 |
|
88 | }
|
89 |
|
90 | Vue.prototype.$forceUpdate = function () {
|
91 | const vm: Component = this
|
92 | if (vm._watcher) {
|
93 | vm._watcher.update()
|
94 | }
|
95 | }
|
96 |
|
97 | Vue.prototype.$destroy = function () {
|
98 | const vm: Component = this
|
99 | if (vm._isBeingDestroyed) {
|
100 | return
|
101 | }
|
102 | callHook(vm, 'beforeDestroy')
|
103 | vm._isBeingDestroyed = true
|
104 |
|
105 | const parent = vm.$parent
|
106 | if (parent && !parent._isBeingDestroyed && !vm.$options.abstract) {
|
107 | remove(parent.$children, vm)
|
108 | }
|
109 |
|
110 | if (vm._watcher) {
|
111 | vm._watcher.teardown()
|
112 | }
|
113 | let i = vm._watchers.length
|
114 | while (i--) {
|
115 | vm._watchers[i].teardown()
|
116 | }
|
117 |
|
118 |
|
119 | if (vm._data.__ob__) {
|
120 | vm._data.__ob__.vmCount--
|
121 | }
|
122 |
|
123 | vm._isDestroyed = true
|
124 |
|
125 | vm.__patch__(vm._vnode, null)
|
126 |
|
127 | callHook(vm, 'destroyed')
|
128 |
|
129 | vm.$off()
|
130 |
|
131 | if (vm.$el) {
|
132 | vm.$el.__vue__ = null
|
133 | }
|
134 |
|
135 | if (vm.$vnode) {
|
136 | vm.$vnode.parent = null
|
137 | }
|
138 | }
|
139 | }
|
140 |
|
141 | export function mountComponent (
|
142 | vm: Component,
|
143 | el: ?Element,
|
144 | hydrating?: boolean
|
145 | ): Component {
|
146 | vm.$el = el
|
147 | if (!vm.$options.render) {
|
148 | vm.$options.render = createEmptyVNode
|
149 | if (process.env.NODE_ENV !== 'production') {
|
150 |
|
151 | if ((vm.$options.template && vm.$options.template.charAt(0) !== '#') ||
|
152 | vm.$options.el || el) {
|
153 | warn(
|
154 | 'You are using the runtime-only build of Vue where the template ' +
|
155 | 'compiler is not available. Either pre-compile the templates into ' +
|
156 | 'render functions, or use the compiler-included build.',
|
157 | vm
|
158 | )
|
159 | } else {
|
160 | warn(
|
161 | 'Failed to mount component: template or render function not defined.',
|
162 | vm
|
163 | )
|
164 | }
|
165 | }
|
166 | }
|
167 | callHook(vm, 'beforeMount')
|
168 |
|
169 | let updateComponent
|
170 |
|
171 | if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
|
172 | updateComponent = () => {
|
173 | const name = vm._name
|
174 | const id = vm._uid
|
175 | const startTag = `vue-perf-start:${id}`
|
176 | const endTag = `vue-perf-end:${id}`
|
177 |
|
178 | mark(startTag)
|
179 | const vnode = vm._render()
|
180 | mark(endTag)
|
181 | measure(`vue ${name} render`, startTag, endTag)
|
182 |
|
183 | mark(startTag)
|
184 | vm._update(vnode, hydrating)
|
185 | mark(endTag)
|
186 | measure(`vue ${name} patch`, startTag, endTag)
|
187 | }
|
188 | } else {
|
189 | updateComponent = () => {
|
190 | vm._update(vm._render(), hydrating)
|
191 | }
|
192 | }
|
193 |
|
194 |
|
195 |
|
196 |
|
197 | new Watcher(vm, updateComponent, noop, {
|
198 | before () {
|
199 | if (vm._isMounted && !vm._isDestroyed) {
|
200 | callHook(vm, 'beforeUpdate')
|
201 | }
|
202 | }
|
203 | }, true )
|
204 | hydrating = false
|
205 |
|
206 |
|
207 |
|
208 | if (vm.$vnode == null) {
|
209 | vm._isMounted = true
|
210 | callHook(vm, 'mounted')
|
211 | }
|
212 | return vm
|
213 | }
|
214 |
|
215 | export function updateChildComponent (
|
216 | vm: Component,
|
217 | propsData: ?Object,
|
218 | listeners: ?Object,
|
219 | parentVnode: MountedComponentVNode,
|
220 | renderChildren: ?Array<VNode>
|
221 | ) {
|
222 | if (process.env.NODE_ENV !== 'production') {
|
223 | isUpdatingChildComponent = true
|
224 | }
|
225 |
|
226 |
|
227 |
|
228 |
|
229 |
|
230 |
|
231 |
|
232 | const hasDynamicScopedSlot = !!(
|
233 | (parentVnode.data.scopedSlots && !parentVnode.data.scopedSlots.$stable) ||
|
234 | (vm.$scopedSlots !== emptyObject && !vm.$scopedSlots.$stable)
|
235 | )
|
236 |
|
237 |
|
238 |
|
239 | const needsForceUpdate = !!(
|
240 | renderChildren ||
|
241 | vm.$options._renderChildren ||
|
242 | hasDynamicScopedSlot
|
243 | )
|
244 |
|
245 | vm.$options._parentVnode = parentVnode
|
246 | vm.$vnode = parentVnode
|
247 |
|
248 | if (vm._vnode) {
|
249 | vm._vnode.parent = parentVnode
|
250 | }
|
251 | vm.$options._renderChildren = renderChildren
|
252 |
|
253 |
|
254 |
|
255 |
|
256 | vm.$attrs = parentVnode.data.attrs || emptyObject
|
257 | vm.$listeners = listeners || emptyObject
|
258 |
|
259 |
|
260 | if (propsData && vm.$options.props) {
|
261 | toggleObserving(false)
|
262 | const props = vm._props
|
263 | const propKeys = vm.$options._propKeys || []
|
264 | for (let i = 0; i < propKeys.length; i++) {
|
265 | const key = propKeys[i]
|
266 | const propOptions: any = vm.$options.props
|
267 | props[key] = validateProp(key, propOptions, propsData, vm)
|
268 | }
|
269 | toggleObserving(true)
|
270 |
|
271 | vm.$options.propsData = propsData
|
272 | }
|
273 |
|
274 |
|
275 | listeners = listeners || emptyObject
|
276 | const oldListeners = vm.$options._parentListeners
|
277 | vm.$options._parentListeners = listeners
|
278 | updateComponentListeners(vm, listeners, oldListeners)
|
279 |
|
280 |
|
281 | if (needsForceUpdate) {
|
282 | vm.$slots = resolveSlots(renderChildren, parentVnode.context)
|
283 | vm.$forceUpdate()
|
284 | }
|
285 |
|
286 | if (process.env.NODE_ENV !== 'production') {
|
287 | isUpdatingChildComponent = false
|
288 | }
|
289 | }
|
290 |
|
291 | function isInInactiveTree (vm) {
|
292 | while (vm && (vm = vm.$parent)) {
|
293 | if (vm._inactive) return true
|
294 | }
|
295 | return false
|
296 | }
|
297 |
|
298 | export function activateChildComponent (vm: Component, direct?: boolean) {
|
299 | if (direct) {
|
300 | vm._directInactive = false
|
301 | if (isInInactiveTree(vm)) {
|
302 | return
|
303 | }
|
304 | } else if (vm._directInactive) {
|
305 | return
|
306 | }
|
307 | if (vm._inactive || vm._inactive === null) {
|
308 | vm._inactive = false
|
309 | for (let i = 0; i < vm.$children.length; i++) {
|
310 | activateChildComponent(vm.$children[i])
|
311 | }
|
312 | callHook(vm, 'activated')
|
313 | }
|
314 | }
|
315 |
|
316 | export function deactivateChildComponent (vm: Component, direct?: boolean) {
|
317 | if (direct) {
|
318 | vm._directInactive = true
|
319 | if (isInInactiveTree(vm)) {
|
320 | return
|
321 | }
|
322 | }
|
323 | if (!vm._inactive) {
|
324 | vm._inactive = true
|
325 | for (let i = 0; i < vm.$children.length; i++) {
|
326 | deactivateChildComponent(vm.$children[i])
|
327 | }
|
328 | callHook(vm, 'deactivated')
|
329 | }
|
330 | }
|
331 |
|
332 | export function callHook (vm: Component, hook: string) {
|
333 |
|
334 | pushTarget()
|
335 | const handlers = vm.$options[hook]
|
336 | const info = `${hook} hook`
|
337 | if (handlers) {
|
338 | for (let i = 0, j = handlers.length; i < j; i++) {
|
339 | invokeWithErrorHandling(handlers[i], vm, null, vm, info)
|
340 | }
|
341 | }
|
342 | if (vm._hasHookEvent) {
|
343 | vm.$emit('hook:' + hook)
|
344 | }
|
345 | popTarget()
|
346 | }
|