广告
返回顶部
首页 > 资讯 > 前端开发 > JavaScript >一文带你理解 Vue 中的生命周期
  • 631
分享到

一文带你理解 Vue 中的生命周期

2024-04-02 19:04:59 631人浏览 八月长安
摘要

目录1、beforeCreate & created2、beforeMount & mounted3、beforeUpdate & updated4

前言:

每个 Vue 实例在被创建之前都要经过一系列的初始化过程。例如需要设置数据监听、编译模板、挂载实例到 DOM、在数据变化时更新 DOM 等。同时在这个过程中也会运行一些叫做生命周期钩子的函数,给予用户机会在一些特定的场景下添加他们自己的代码。

源码中最终执行生命周期的函数都是调用 callHook 方法,它的定义在 src/core/instance/lifecycle 中:


export function callHook (vm: Component, hook: string) {
 // #7573 disable dep collection when invoking lifecycle hooks
  pushTarget()
  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)
  }
  popTarget()
}


callHook 函数的逻辑很简单,根据传入的字符串 hook,去拿到 vm.$options[hook] 对应的回调函数数组,然后遍历执行,执行的时候把 vm 作为函数执行的上下文。

1、beforeCreate & created

beforeCreate created 函数都是在实例化 Vue 的阶段,在 _init 方法中执行的,它的定义在 src/core/instance/init.js 中:


Vue.prototype._init = function (options?: Object) {
  // ...
  initLifecycle(vm)
  initEvents(vm)
  initRender(vm)
  callHook(vm, 'beforeCreate')
  initInjections(vm) // resolve injections before data/props
  initState(vm)
  initProvide(vm) // resolve provide after data/props
  callHook(vm, 'created')
  // ...
}


可以看到 beforeCreate created 的钩子调用是在 initState 的前后,initState 的作用是初始化 propsdatamethodswatchcomputed 等属性,之后我们会详细分析。那么显然 beforeCreate 的钩子函数中就不能获取到 propsdata 中定义的值,也不能调用 methods 中定义的函数。

在这俩个钩子函数执行的时候,并没有渲染 DOM,所以我们也不能够访问 DOM,一般来说,如果组件在加载的时候需要和后端有交互,放在这俩个钩子函数执行都可以,如果是需要访问 propsdata 等数据的话,就需要使用 created 钩子函数。之后我们会介绍 vue-router 和 vuex 的时候会发现它们都混合了 beforeCreatd 钩子函数。

2、beforeMount & mounted

顾名思义,beforeMount 钩子函数发生在 mount,也就是 DOM 挂载之前,它的调用时机是在 mountComponent 函数中,定义在 src/core/instance/lifecycle.js 中:


export function mountComponent (
  vm: Component,
  el: ?Element,
  hydrating?: boolean
): Component {
  vm.$el = el
  // ...
  callHook(vm, 'beforeMount')
 
  let updateComponent
  
  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) {
        callHook(vm, 'beforeUpdate')
      }
    }
  }, true )
  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
}


在执行 vm. render() 函数渲染 VNode 之前,执行了 beforeMount 钩子函数,在执行完 vm. update() VNode patch 到真实 DOM 后,执行 mouted 钩子。注意,这里对 mouted 钩子函数执行有一个判断逻辑,vm.$vnode 如果为 null,则表明这不是一次组件的初始化过程,而是我们通过外部 new Vue 初始化过程。那么对于组件,它的 mounted 时机在哪儿呢?

组件的 VNode patch 到 DOM 后,会执行 invokeInsertHook 函数,把 insertedVnodeQueue 里保存的钩子函数依次执行一遍,它的定义在 src/core/vdom/patch.js 中:


function invokeInsertHook (vnode, queue, initial) {
 // delay insert hooks for component root nodes, invoke them after the
  // element is really inserted
  if (isTrue(initial) && isDef(vnode.parent)) {
    vnode.parent.data.pendingInsert = queue
  } else {
    for (let i = 0; i < queue.length; ++i) {
      queue[i].data.hook.insert(queue[i])
    }
  }
}


该函数会执行 insert 这个钩子函数,对于组件而言,insert 钩子函数的定义在 src/core/vdom/create-component.js 中的 componentVNodeHooks 中:


const componentVNodeHooks = {
  // ...
  insert (vnode: MountedComponentVNode) {
    const { context, componentInstance } = vnode
    if (!componentInstance._isMounted) {
      componentInstance._isMounted = true
      callHook(componentInstance, 'mounted')
    }
    // ...
  },
}


我们可以看到,每个子组件都是在这个钩子函数中执行 mouted 钩子函数,并且我们之前分析过,insertedVnodeQueue 的添加顺序是先子后父,所以对于同步渲染的子组件而言,mounted 钩子函数的执行顺序也是先子后父。

3、beforeUpdate & updated

顾名思义,beforeUpdate updated 的钩子函数执行时机都应该是在数据更新的时候,到目前为止,我们还没有分析 Vue 的数据双向绑定、更新相关,下一章我会详细介绍这个过程。

beforeUpdate 的执行时机是在渲染 Watcher before 函数中


export function mountComponent (
  vm: Component,
  el: ?Element,
  hydrating?: boolean
): Component {
  // ...
 
  // 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) {
        callHook(vm, 'beforeUpdate')
      }
    }
  }, true )
  // ...
}


注意这里有个判断,也就是在组件已经 mounted 之后,才会去调用这个钩子函数。

update 的执行时机是在flushSchedulerQueue 函数调用的时候, 它的定义在 src/core/observer/scheduler.js 中:


function flushSchedulerQueue () {
  // ...
  // 获取到 updatedQueue
  callUpdatedHooks(updatedQueue)
}
 
function callUpdatedHooks (queue) {
  let i = queue.length
  while (i--) {
    const watcher = queue[i]
    const vm = watcher.vm
    if (vm._watcher === watcher && vm._isMounted) {
      callHook(vm, 'updated')
    }
  }
}


flushSchedulerQueue 函数我们之后会详细介绍,可以先大概了解一下,updatedQueue 是 更新了的 wathcer 数组,那么在 callUpdatedHooks 函数中,它对这些数组做遍历,只有满足当前 watcher vm._watcher 以及组件已经 mounted 这两个条件,才会执行 updated 钩子函数。

我们之前提过,在组件 mount 的过程中,会实例化一个渲染的 Watcher 去监听 vm 上的数据变化重新渲染,这断逻辑发生在 mountComponent 函数执行的时候:


export function mountComponent (
  vm: Component,
  el: ?Element,
  hydrating?: boolean
): Component {
  // ...
  // 这里是简写
  let updateComponent = () => {
      vm._update(vm._render(), hydrating)
  }
  new Watcher(vm, updateComponent, noop, {
    before () {
      if (vm._isMounted) {
        callHook(vm, 'beforeUpdate')
      }
    }
  }, true )
  // ...
}


那么在实例化 Watcher 的过程中,在它的构造函数里会判断 isRenderWatcher,接着把当前 watcher 的实例赋值给 vm._watcher,定义在 src/core/observer/watcher.js 中:


export default class Watcher {
  // ...
  constructor (
    vm: Component,
    expOrFn: string | Function,
    cb: Function,
    options?: ?Object,
    isRenderWatcher?: boolean
  ) {
    this.vm = vm
    if (isRenderWatcher) {
      vm._watcher = this
    }
    vm._watchers.push(this)
    // ...
  }
}


同时,还把当前 wathcer 实例 push 到 vm. watchers 中,vm. watcher 是专门用来监听 vm 上数据变化然后重新渲染的,所以它是一个渲染相关的 watcher,因此在 callUpdatedHooks 函数中,只有 vm._watcher 的回调执行完毕后,才会执行 updated 钩子函数。

4、beforeDestroy & destroyed

顾名思义,beforeDestroy destroyed 钩子函数的执行时机在组件销毁的阶段,组件的销毁过程之后会详细介绍,最终会调用 $destroy 方法,它的定义在 src/core/instance/lifecycle.js 中:


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


beforeDestroy 钩子函数的执行时机是在 destroy函数执行最开始的地方,接着执行了一系列的销毁动作,包括从parentchildren 中删掉自身,删除 watcher,当前渲染的 VNode 执行销毁钩子函数等,执行完毕后再调用 destroy 钩子函数。

在 $destroy 的执行过程中,它又会执行 vm. patch (vm._vnode, null) 触发它子组件的销毁钩子函数,这样一层层的递归调用,所以 destroy 钩子函数执行顺序是先子后父,和 mounted 过程一样。

5、activated & deactivated

activated 和 deactivated 钩子函数是专门为 keep-alive 组件定制的钩子,我们会在介绍 keep-alive 组件的时候详细介绍,这里先留个悬念。

总结:

这一节主要介绍了 Vue 生命周期中各个钩子函数的执行时机以及顺序,通过分析,我们知道了如在 created 钩子函数中可以访问到数据,在 mounted 钩子函数中可以访问到 DOM,在 destroy 钩子函数中可以做一些定时器销毁工作,了解它们有利于我们在合适的生命周期去做不同的事情。

到此这篇关于一文带你理解 Vue 中的生命周期的文章就介绍到这了,更多相关 Vue 中的生命周期内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: 一文带你理解 Vue 中的生命周期

本文链接: https://www.lsjlt.com/news/136516.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 一文带你理解 Vue 中的生命周期
    目录1、beforeCreate & created2、beforeMount & mounted3、beforeUpdate & updated4...
    99+
    2022-11-12
  • 带你一文了解Vue生命周期钩子
    目录前言生命周期钩子选项式API(Options API)生命周期流程图运行生命周期挂钩beforeCreate()created()beforeMount()mounted()be...
    99+
    2022-11-13
  • 一篇文章带你了解Maven的生命周期
    目录1、什么是 生命周期?2、Clean Lifecycle:在进行真正的构建之前进行一些清理工作3、Default Lifecycle:构建的核心部分,编译、测试、打包、安装、部署...
    99+
    2022-11-13
  • Vue生命周期怎么理解
    这篇“Vue生命周期怎么理解”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Vue生命周期怎么理解”文章吧。1、定义生命周期函...
    99+
    2023-07-06
  • 一篇文章带你吃透Vue生命周期(结合案例通俗易懂)
    目录1.vue生命周期1.0_人的-生命周期1.1_钩子函数1.2_初始化阶段1.3_挂载阶段1.4_更新阶段1.5_销毁阶段2.axios2.0_axios基本使用2.1_axio...
    99+
    2022-11-13
  • vue的生命周期钩子与父子组件的生命周期详解
    目录vue的生命周期钩子的介绍父子组件的生命周期加载渲染过程父组件更新过程子组件更新过程父子组件更新过程销毁过程代码示例created和mounted的区别vue的生命周期钩子的介绍...
    99+
    2022-11-13
    vue 生命周期 vue 父子组件生命周期
  • Vue的生命周期一起来看看
    目录1. 生命周期(重要)1.1 初步认识生命周期1.2 生命周期流程(8个)1.3 生命周期详细流程图1.4 常用的生命周期钩子:1.4.1 关于销毁1.4.2 关于父子组件的生命...
    99+
    2022-11-13
  • 一起来学习Vue的生命周期
    目录生命周期生命周期的简单介绍beforeCreate与createdbeforeCreate()created()beforeMount与mountedbeforeMount()m...
    99+
    2022-11-13
  • 一文搞懂Spring中Bean的生命周期
    目录一、使用配置生命周期的方法二、生命周期控制——接口控制(了解)小结生命周期:从创建到消亡的完整过程 bean声明周期:bean从创建到销毁的整体过程 be...
    99+
    2022-11-13
  • Vue生命周期中的组件化你知道吗
    目录引出生命周期销毁流程生命周期生命周期总结组件化 template: 非单文件组件 组件的几个注意点  组件的嵌套 &n...
    99+
    2022-11-13
  • Vue中的生命周期介绍
    什么是vue的生命周期 Vue中的生命周期是指组件从创建到销毁的一系列过程。看下面这张官方文档的图: 从图片中可以看出Vue的整个生命周期包括8个状态,按照先后顺序分别为: bef...
    99+
    2022-11-13
  • vue中的生命周期是什么
    这篇文章给大家分享的是有关vue中的生命周期是什么的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。一、什么是生命周期每一个组件都可能经历从创建,挂载,更新,卸载的过程。在这个过程中的某一个阶段,用于可能会想要添加一...
    99+
    2023-06-29
  • vue中的ajax一般放在什么生命周期中
    这篇文章主要介绍了vue中的ajax一般放在什么生命周期中,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。 vue...
    99+
    2022-10-19
  • JavaSpringBean的生命周期管理详解
    目录Spring Bean的生命周期管理一、Spring Bean的生命周期二、通过@Bean的参数(initMethod ,destroyMethod)指定Bean的初始化和销毁方...
    99+
    2022-11-12
  • React中的生命周期详解
    目录react生命周期常用的生命周期不常用的生命周完整的生命周期图react生命周期 函数组件无生命周期,生命周期只有类组件才拥有 生命周期函数指在某一时刻组件会自动调用并执行的函数...
    99+
    2022-11-13
  • Vue中的生命周期实例分析
    这篇文章主要介绍“Vue中的生命周期实例分析”,在日常操作中,相信很多人在Vue中的生命周期实例分析问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Vue中的生命周期实例分析”的疑惑有所帮助!接下来,请跟着小编...
    99+
    2023-06-29
  • Vue中生命周期的示例分析
    这篇文章将为大家详细讲解有关Vue中生命周期的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。最简单的Vue 实例//html <div id=&q...
    99+
    2022-10-19
  • 如何理解Vue生命周期和钩子函数
    这期内容当中小编将会给大家带来有关如何理解Vue生命周期和钩子函数,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。1. vue生命周期一组件从 创建 到 销毁 的整个过程就是生命周期Vue 实例从创建到销毁...
    99+
    2023-06-25
  • Android Activity生命周期调用的理解
    目录状态启动模式操作APP时生命周期调用Activity异常生命周期总结状态 活动存放在一个叫返回栈的一个集合,当重新打开一个Activity时,它就会出现在栈顶。当要销毁该活动时...
    99+
    2022-11-12
  • 一文搞懂SpringBean中的作用域和生命周期
    目录一、Spring Bean 作用域singleton(单例)prototype(原型)小结二、Spring Bean生命周期如何关闭容器生命周期回调通过接口设置生命周期通过xml...
    99+
    2022-11-13
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作