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


    /* @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)
      }
    }
  • 相关阅读:
    简单理解ThreadLocal原理和适用场景
    Portal实现原理
    Spring cloud微服务实战——基于OAUTH2.0统一认证授权的微服务基础架构
    Java8中 Date和LocalDateTime的相互转换
    sonar rule
    图论篇2——最小生成树算法(kurskal算法&prim算法)
    图论篇1——图的基本概念
    数论篇6——欧拉函数
    数论篇5——数论四大定理
    数论篇4——逆元(数论倒数)
  • 原文地址:https://www.cnblogs.com/dhsz/p/7111866.html
Copyright © 2020-2023  润新知