深入vue2.0源码系列:生命周期的实现

简介: 深入vue2.0源码系列:生命周期的实现

前言

在Vue.js 2.0中,组件的生命周期函数分为8个阶段,分别是:

  1. beforeCreate:组件实例刚被创建,但是数据和事件监听还未被初始化。
  2. created:组件实例已经被创建,并且数据和事件监听已经初始化完成。
  3. beforeMount:组件将要被挂载到页面上,但是还没有开始渲染。
  4. mounted:组件已经被挂载到页面上,并且已经渲染完毕。
  5. beforeUpdate:组件将要被更新,但是还没有开始重新渲染。
  6. updated:组件已经被更新,并且重新渲染完毕。
  7. beforeDestroy:组件将要被销毁,但是还没有开始销毁。
  8. destroyed:组件已经被销毁。

实现过程

在Vue.js 2.0中,生命周期函数的实现主要是通过一个叫做“lifecycle.js”的模块来完成的。下面是该模块的完整代码,包括注释:

/* @flow */
import config from '../config'
import { callHook, activateChildComponent } from '../instance/lifecycle'
import { createEmptyVNode } from '../vdom/vnode'
import { updateComponentListeners } from '../vdom/helpers/index'
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 prevActiveInstance = config.activeInstance
    config.activeInstance = vm
    vm._vnode = vnode
    // Vue.js 的更新流程
    if (!prevVnode) {
      // 如果没有旧的 VNode,说明这是组件的首次渲染
      vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false /* removeOnly */)
    } else {
      // 如果有旧的 VNode,说明这是组件的更新过程
      vm.$el = vm.__patch__(prevVnode, vnode)
    }
    config.activeInstance = prevActiveInstance
    // update __vue__ reference
    if (prevEl) {
      prevEl.__vue__ = null
    }
    if (vm.$el) {
      vm.$el.__vue__ = vm
      // 如果组件已经被挂载,并且有子组件,则需要激活子组件的钩子函数
      if (vm.$vnode && vm.$parent && vm.$vnode === vm.$parent._vnode) {
        activateChildComponent(vm.$vnode, vm.$options._renderChildren, vm)
      }
      // 触发组件的 updated 钩子函数
      callHook(vm, 'updated')
    }
  }
  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
    }
    // 触发组件的 beforeDestroy 钩子函数
    callHook(vm, 'beforeDestroy')
    vm._isBeingDestroyed = true
    // 如果组件有父组件,则需要将其从父组件的 $children 中删除
    if (vm.$parent) {
      const index = vm.$parent.$children.indexOf(vm)
      if (index > -1) {
        vm.$parent.$children.splice(index, 1)
      }
    }
    // 销毁组件上的所有监听器
    vm._watcher && vm._watcher.teardown()
    let i = vm._watchers.length
    while (i--) {
      vm._watchers[i].teardown()
    }
    // 删除组件上所有的属性和方法
    if (vm._data.__ob__) {
      vm._data.__ob__.vmCount--
    }
    vm._isDestroyed = true
    // 触发组件的 destroyed 钩子函数
    callHook(vm, 'destroyed')
      Vue.prototype.$nextTick = function (fn: Function) {
    return nextTick(fn, this)
  }
  Vue.prototype._render = function (): VNode {
    const vm: Component = this
    const { render, _parentVnode } = vm.$options
    if (_parentVnode) {
      vm.$scopedSlots = _parentVnode.data.scopedSlots || emptyObject
    }
    // 设置父节点,用于在子组件中使用 $parent
    vm.$vnode = _parentVnode
    // 渲染 VNode
    let vnode
    try {
      // 调用渲染函数,得到组件的 VNode
      vnode = render.call(vm._renderProxy, vm.$createElement)
    } catch (e) {
      handleError(e, vm, `render`)
      vnode = vm._vnode || createEmptyVNode()
    }
    // 如果返回的是一个数组,则只取第一个元素作为 VNode
    if (Array.isArray(vnode) && vnode.length === 1) {
      vnode = vnode[0]
    }
    // 判断是否为合法的 VNode,如果不是,则创建一个空的 VNode
    if (!(vnode instanceof VNode)) {
      vnode = createEmptyVNode()
    }
    // 设置组件的父子关系
    vnode.parent = _parentVnode
    return vnode
  }
}
// Vue 实例的生命周期钩子函数
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)
  }
  // 设置当前实例的 $parent、$root、$children 属性
  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
    // 如果已经渲染过了,则会调用 beforeUpdate 钩子函数
    if (vm._isMounted) {
      callHook(vm, 'beforeUpdate')
    }
    // 保存旧的 VNode
    const prevEl = vm.$el
    const prevVnode = vm._vnode
    const prevActiveInstance = activeInstance
    activeInstance = vm
    vm._vnode = vnode
    // 如果之前没有 VNode,则直接渲染
    if (!prevVnode) {
      vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false /* removeOnly */)
    } else {
      // 如果有旧的 VNode,则对比新旧 VNode,然后更新
      vm.$el = vm.__patch__(prevVnode, vnode)
    }
    activeInstance = prevActiveInstance
    // 更新完毕之后,会调用 updated 钩子函数
    if (prevEl) {
      vm.$forceUpdate()
      callHook(vm, 'updated')
    } else if (vm.$vnode == null) {
      callHook(vm, 'mounted')
      vm._isMounted = true
    }
    return vm
  }
  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
    }
    // 触发 beforeDestroy 钩子函数
    callHook(vm, 'beforeDestroy')
    // 标记当前实例正在被销毁
    vm._isBeingDestroyed = true
    // 从父组件的 $children 数组中移除当前实例
    const parent = vm.$parent
    if (parent && !parent._isBeingDestroyed && !vm.$options.abstract) {
      remove(parent.$children, vm)
    }
    // 卸载所有的 watcher
    if (vm._watcher) {
      vm._watcher.teardown()
    }
    let i = vm._watchers.length
    while (i--) {
      vm._watchers[i].teardown()
    }
    // remove reference from data
    // remove reference from data ob
    // frozen object may not have observer.
    if (vm._data.__ob__) {
      vm._data.__ob__.vmCount--
    }
    // 使当前实例处于销毁状态
    vm._isDestroyed = true
    // 触发 destroyed 钩子函数
    callHook(vm, 'destroyed')
    // 释放所有占用的资源
    vm.$off()
    // 卸载所有子组件
    if (vm.$options._parentElm && vm.$options._refElm) {
      vm.$options._parentElm.removeChild(vm.$options._refElm)
    }
    // 解除父子关系
    vm.$parent = null
    vm.$root = null
    // 移除所有监听器
    vm.$listeners = {}
    // 移除所有实例属性
    vm._watchers = []
    vm._data = {}
    vm._props = {}
    vm._methods = {}
    vm._computedWatchers = {}
    // 标记当前实例已被销毁
    vm._renderProxy = null
    vm._staticTrees = null
    // 禁用当前实例
    vm._disable = true
  }
}
// 触发钩子函数
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)
  }
}

在上面的代码中,我们可以看到 initLifecycle 函数会设置当前实例的 $parent、$root、$children 和 $refs 属性。它还会查找当前实例的第一个非抽象父组件,并将当前实例添加到父组件的 $children 数组中。lifecycleMixin 函数实现了 Vue 实例的更新、强制更新和销毁。它还定义了 callHook 函数来触发生命周期钩子函数。

总结

Vue.js 的生命周期钩子函数是在 Vue 实例的不同阶段执行的回调函数。通过使用这些钩子函数,我们可以在 Vue 实例的不同生命周期阶段执行自定义代码。


Vue 2.x 的生命周期钩子函数包括 beforeCreate、created、beforeMount、mounted、beforeUpdate、updated、beforeDestroy 和 destroyed。这些钩子函数按照执行顺序分为三个阶段:创建阶段、挂载阶段和销毁阶段。


Vue 3.x 中的生命周期钩子函数与 Vue 2.x 中的类似,但是 beforeCreate 和 created 钩子函数的实现有所不同。Vue 3.x 使用 setup 函数来代替 beforeCreate 和 created 钩子函数。


在 Vue.js 源码中,生命周期钩子函数的实现是在 lifecycleMixin 函数中完成的。lifecycleMixin 函数实现了 Vue 实例的更新、强制更新和销毁,并定义了 callHook 函数来触发生命周期钩子函数。


通过深入理解 Vue.js 的生命周期钩子函数实现,我们可以更好地理解 Vue.js 的运行机制,并且可以在自己的 Vue.js 项目中更加灵活地使用生命周期钩子函数来实现自定义功能。


相关文章
|
3月前
|
JavaScript 开发者
[译] 监听第三方 Vue 组件的生命周期钩子
[译] 监听第三方 Vue 组件的生命周期钩子
|
1月前
|
JavaScript
Vue 的父组件和子组件生命周期钩子执行顺序
在 Vue 中,父组件和子组件的生命周期钩子执行顺序如下:
|
1月前
|
JavaScript
|
1月前
|
JavaScript
vue3 生命周期
【10月更文挑战第14天】vue3 生命周期
|
5月前
|
JavaScript
vue的生命周期
vue的生命周期
33 3
|
5月前
|
JavaScript 前端开发
vue的生命周期
vue的生命周期
36 2
|
1月前
|
开发者
vue3生命周期钩子变化
【10月更文挑战第4天】
|
1月前
|
JavaScript 前端开发 API
深入探索挖掘vue3 生命周期
【10月更文挑战第10天】
33 0
|
2月前
|
JavaScript 前端开发 API
Vue3基础(十yi)___常用生命周期函数___setup___onMounted___onUpdated
本文介绍了Vue 3中的常用生命周期函数,包括`setup`、`onBeforeMount`、`onMounted`、`onBeforeUpdate`、`onUpdated`、`onBeforeUnmount`和`onUnmounted`,并解释了它们与Vue 2生命周期钩子的对应关系。文章通过代码示例展示了这些生命周期钩子在组件中的使用时机和场景。
44 0
|
3月前
|
JavaScript 前端开发 程序员
Vue学习之--------Vue生命周期beforeCreate、created、beforeMount、mounted、beforeDestroy 。。。(图解详细过程)(2022/7/17)
这篇文章详细介绍了Vue的生命周期和各个阶段的钩子函数,包括`beforeCreate`、`created`、`beforeMount`、`mounted`、`beforeUpdate`、`updated`、`beforeDestroy`和`destroyed`。文章通过图解、方法说明、代码实例和测试效果,阐述了每个钩子函数的作用和使用场景,帮助读者深入理解Vue实例从创建到销毁的整个过程。
Vue学习之--------Vue生命周期beforeCreate、created、beforeMount、mounted、beforeDestroy 。。。(图解详细过程)(2022/7/17)
下一篇
无影云桌面