vue.js 源代码学习笔记 ----- core lifecycle

vue

/* @flow */

import config from '../config'

import Watcher from '../observer/watcher'

import { mark, measure } from '../util/perf'

import { createEmptyVNode } from '../vdom/vnode'

import { observerState } from '../observer/index'

import { updateComponentListeners } from './events'

import { resolveSlots } from './render-helpers/resolve-slots'

import {

warn,

noop,

remove,

handleError,

emptyObject,

validateProp

} from '../util/index'

export let activeInstance: any = null

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

if (vm._isMounted) {

callHook(vm, 'beforeUpdate')

}

const prevEl = vm.$el

const prevVnode = vm._vnode

const prevActiveInstance = activeInstance

activeInstance = 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 */,

vm.$options._parentElm,

vm.$options._refElm

)

} else {

// updates

vm.$el = vm.__patch__(prevVnode, vnode)

}

activeInstance = prevActiveInstance

// 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

}

// remove reference to DOM nodes (prevents leak)

vm.$options._parentElm = vm.$options._refElm = 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(`${name} render`, startTag, endTag)

mark(startTag)

vm._update(vnode, hydrating)

mark(endTag)

measure(`${name} patch`, startTag, endTag)

}

} else {

updateComponent = () => {

vm._update(vm._render(), hydrating)

}

}

vm._watcher = new Watcher(vm, updateComponent, noop)

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: VNode,

renderChildren: ?Array<VNode>

) {

// determine whether component has slot children

// we need to do this before overwriting $options._renderChildren

const hasChildren = !!(

renderChildren || // has new static slots

vm.$options._renderChildren || // has old static slots

parentVnode.data.scopedSlots || // has new scoped slots

vm.$scopedSlots !== emptyObject // has old scoped slots

)

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 props

if (propsData && vm.$options.props) {

observerState.shouldConvert = false

if (process.env.NODE_ENV !== 'production') {

observerState.isSettingProps = true

}

const props = vm._props

const propKeys = vm.$options._propKeys || []

for (let i = 0; i < propKeys.length; i++) {

const key = propKeys[i]

props[key] = validateProp(key, vm.$options.props, propsData, vm)

}

observerState.shouldConvert = true

if (process.env.NODE_ENV !== 'production') {

observerState.isSettingProps = false

}

// keep a copy of raw propsData

vm.$options.propsData = propsData

}

// update listeners

if (listeners) {

const oldListeners = vm.$options._parentListeners

vm.$options._parentListeners = listeners

updateComponentListeners(vm, listeners, oldListeners)

}

// resolve slots + force update if has children

if (hasChildren) {

vm.$slots = resolveSlots(renderChildren, parentVnode.context)

vm.$forceUpdate()

}

}

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) {

const handlers = vm.$options[hook]

if (handlers) {

for (let i = 0, j = handlers.length; i < j; i++) {

try {

handlers[i].call(vm)

} catch (e) {

handleError(e, vm, `${hook} hook`)

}

}

}

if (vm._hasHookEvent) {

vm.$emit('hook:' + hook)

}

}

以上是 vue.js 源代码学习笔记 ----- core lifecycle 的全部内容, 来源链接: utcz.com/z/377535.html

回到顶部