123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350 |
- /* @flow */
- import config from '../config'
- import Watcher from '../observer/watcher'
- import { mark, measure } from '../util/perf'
- import { createEmptyVNode } from '../vdom/vnode'
- import { updateComponentListeners } from './events'
- import { resolveSlots } from './render-helpers/resolve-slots'
- import { toggleObserving } from '../observer/index'
- import { pushTarget, popTarget } from '../observer/dep'
- import {
- warn,
- noop,
- remove,
- emptyObject,
- validateProp,
- invokeWithErrorHandling
- } from '../util/index'
- export let activeInstance: any = null
- export let isUpdatingChildComponent: boolean = false
- export function setActiveInstance(vm: Component) {
- const prevActiveInstance = activeInstance
- activeInstance = vm
- return () => {
- activeInstance = prevActiveInstance
- }
- }
- export function initLifecycle (vm: Component) {
- const options = vm.$options
- // locate first non-abstract parent
- let parent = options.parent
- if (parent && !options.abstract) {
- while (parent.$options.abstract && parent.$parent) {
- parent = parent.$parent
- }
- parent.$children.push(vm)
- }
- vm.$parent = parent
- vm.$root = parent ? parent.$root : vm
- vm.$children = []
- vm.$refs = {}
- vm._watcher = null
- vm._inactive = null
- vm._directInactive = false
- vm._isMounted = false
- vm._isDestroyed = false
- vm._isBeingDestroyed = false
- }
- export function lifecycleMixin (Vue: Class<Component>) {
- Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) {
- const vm: Component = this
- const prevEl = vm.$el
- const prevVnode = vm._vnode
- const restoreActiveInstance = setActiveInstance(vm)
- vm._vnode = vnode
- // Vue.prototype.__patch__ is injected in entry points
- // based on the rendering backend used.
- if (!prevVnode) {
- // initial render
- vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false /* removeOnly */)
- } else {
- // updates
- vm.$el = vm.__patch__(prevVnode, vnode)
- }
- restoreActiveInstance()
- // update __vue__ reference
- if (prevEl) {
- prevEl.__vue__ = null
- }
- if (vm.$el) {
- vm.$el.__vue__ = vm
- }
- // if parent is an HOC, update its $el as well
- if (vm.$vnode && vm.$parent && vm.$vnode === vm.$parent._vnode) {
- vm.$parent.$el = vm.$el
- }
- // updated hook is called by the scheduler to ensure that children are
- // updated in a parent's updated hook.
- }
- Vue.prototype.$forceUpdate = function () {
- const vm: Component = this
- if (vm._watcher) {
- vm._watcher.update()
- }
- }
- Vue.prototype.$destroy = function () {
- const vm: Component = this
- if (vm._isBeingDestroyed) {
- return
- }
- callHook(vm, 'beforeDestroy')
- vm._isBeingDestroyed = true
- // remove self from parent
- const parent = vm.$parent
- if (parent && !parent._isBeingDestroyed && !vm.$options.abstract) {
- remove(parent.$children, vm)
- }
- // teardown watchers
- if (vm._watcher) {
- vm._watcher.teardown()
- }
- let i = vm._watchers.length
- while (i--) {
- vm._watchers[i].teardown()
- }
- // remove reference from data ob
- // frozen object may not have observer.
- if (vm._data.__ob__) {
- vm._data.__ob__.vmCount--
- }
- // call the last hook...
- vm._isDestroyed = true
- // invoke destroy hooks on current rendered tree
- vm.__patch__(vm._vnode, null)
- // fire destroyed hook
- callHook(vm, 'destroyed')
- // turn off all instance listeners.
- vm.$off()
- // remove __vue__ reference
- if (vm.$el) {
- vm.$el.__vue__ = null
- }
- // release circular reference (#6759)
- if (vm.$vnode) {
- vm.$vnode.parent = null
- }
- }
- }
- export function mountComponent (
- vm: Component,
- el: ?Element,
- hydrating?: boolean
- ): Component {
- vm.$el = el
- if (!vm.$options.render) {
- vm.$options.render = createEmptyVNode
- if (process.env.NODE_ENV !== 'production') {
- /* istanbul ignore if */
- if ((vm.$options.template && vm.$options.template.charAt(0) !== '#') ||
- vm.$options.el || el) {
- warn(
- 'You are using the runtime-only build of Vue where the template ' +
- 'compiler is not available. Either pre-compile the templates into ' +
- 'render functions, or use the compiler-included build.',
- vm
- )
- } else {
- warn(
- 'Failed to mount component: template or render function not defined.',
- vm
- )
- }
- }
- }
- callHook(vm, 'beforeMount')
- let updateComponent
- /* istanbul ignore if */
- if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
- updateComponent = () => {
- const name = vm._name
- const id = vm._uid
- const startTag = `vue-perf-start:${id}`
- const endTag = `vue-perf-end:${id}`
- mark(startTag)
- const vnode = vm._render()
- mark(endTag)
- measure(`vue ${name} render`, startTag, endTag)
- mark(startTag)
- vm._update(vnode, hydrating)
- mark(endTag)
- measure(`vue ${name} patch`, startTag, endTag)
- }
- } else {
- updateComponent = () => {
- vm._update(vm._render(), hydrating)
- }
- }
- // we set this to vm._watcher inside the watcher's constructor
- // since the watcher's initial patch may call $forceUpdate (e.g. inside child
- // component's mounted hook), which relies on vm._watcher being already defined
- new Watcher(vm, updateComponent, noop, {
- before () {
- if (vm._isMounted && !vm._isDestroyed) {
- callHook(vm, 'beforeUpdate')
- }
- }
- }, true /* isRenderWatcher */)
- hydrating = false
- // manually mounted instance, call mounted on self
- // mounted is called for render-created child components in its inserted hook
- if (vm.$vnode == null) {
- vm._isMounted = true
- callHook(vm, 'mounted')
- }
- return vm
- }
- export function updateChildComponent (
- vm: Component,
- propsData: ?Object,
- listeners: ?Object,
- parentVnode: MountedComponentVNode,
- renderChildren: ?Array<VNode>
- ) {
- if (process.env.NODE_ENV !== 'production') {
- isUpdatingChildComponent = true
- }
- // determine whether component has slot children
- // we need to do this before overwriting $options._renderChildren.
- // check if there are dynamic scopedSlots (hand-written or compiled but with
- // dynamic slot names). Static scoped slots compiled from template has the
- // "$stable" marker.
- const newScopedSlots = parentVnode.data.scopedSlots
- const oldScopedSlots = vm.$scopedSlots
- const hasDynamicScopedSlot = !!(
- (newScopedSlots && !newScopedSlots.$stable) ||
- (oldScopedSlots !== emptyObject && !oldScopedSlots.$stable) ||
- (newScopedSlots && vm.$scopedSlots.$key !== newScopedSlots.$key)
- )
- // Any static slot children from the parent may have changed during parent's
- // update. Dynamic scoped slots may also have changed. In such cases, a forced
- // update is necessary to ensure correctness.
- const needsForceUpdate = !!(
- renderChildren || // has new static slots
- vm.$options._renderChildren || // has old static slots
- hasDynamicScopedSlot
- )
- vm.$options._parentVnode = parentVnode
- vm.$vnode = parentVnode // update vm's placeholder node without re-render
- if (vm._vnode) { // update child tree's parent
- vm._vnode.parent = parentVnode
- }
- vm.$options._renderChildren = renderChildren
- // update $attrs and $listeners hash
- // these are also reactive so they may trigger child update if the child
- // used them during render
- vm.$attrs = parentVnode.data.attrs || emptyObject
- vm.$listeners = listeners || emptyObject
- // update props
- if (propsData && vm.$options.props) {
- toggleObserving(false)
- const props = vm._props
- const propKeys = vm.$options._propKeys || []
- for (let i = 0; i < propKeys.length; i++) {
- const key = propKeys[i]
- const propOptions: any = vm.$options.props // wtf flow?
- props[key] = validateProp(key, propOptions, propsData, vm)
- }
- toggleObserving(true)
- // keep a copy of raw propsData
- vm.$options.propsData = propsData
- }
- // update listeners
- listeners = listeners || emptyObject
- const oldListeners = vm.$options._parentListeners
- vm.$options._parentListeners = listeners
- updateComponentListeners(vm, listeners, oldListeners)
- // resolve slots + force update if has children
- if (needsForceUpdate) {
- vm.$slots = resolveSlots(renderChildren, parentVnode.context)
- vm.$forceUpdate()
- }
- if (process.env.NODE_ENV !== 'production') {
- isUpdatingChildComponent = false
- }
- }
- function isInInactiveTree (vm) {
- while (vm && (vm = vm.$parent)) {
- if (vm._inactive) return true
- }
- return false
- }
- export function activateChildComponent (vm: Component, direct?: boolean) {
- if (direct) {
- vm._directInactive = false
- if (isInInactiveTree(vm)) {
- return
- }
- } else if (vm._directInactive) {
- return
- }
- if (vm._inactive || vm._inactive === null) {
- vm._inactive = false
- for (let i = 0; i < vm.$children.length; i++) {
- activateChildComponent(vm.$children[i])
- }
- callHook(vm, 'activated')
- }
- }
- export function deactivateChildComponent (vm: Component, direct?: boolean) {
- if (direct) {
- vm._directInactive = true
- if (isInInactiveTree(vm)) {
- return
- }
- }
- if (!vm._inactive) {
- vm._inactive = true
- for (let i = 0; i < vm.$children.length; i++) {
- deactivateChildComponent(vm.$children[i])
- }
- callHook(vm, 'deactivated')
- }
- }
- export function callHook (vm: Component, hook: string) {
- // #7573 disable dep collection when invoking lifecycle hooks
- pushTarget()
- const handlers = vm.$options[hook]
- const info = `${hook} hook`
- if (handlers) {
- for (let i = 0, j = handlers.length; i < j; i++) {
- invokeWithErrorHandling(handlers[i], vm, null, vm, info)
- }
- }
- if (vm._hasHookEvent) {
- vm.$emit('hook:' + hook)
- }
- popTarget()
- }
|