当前位置: 首页 > ds >正文

Vue 生命周期详解

前言

vue的生命周期,是vue中比较重要的知识点,在面试中也经常会被问到,所以就打算写一篇关于vue生命周期的文章。

生命周期介绍

世间万物都有自己生命周期⏱,vue也有这一特点,vue的生命周期可以简单分为四个阶段:创建阶段,挂载阶段,运行阶段,销毁阶段。

每个 Vue 实例在被创建时都要经过一系列的初始化过程——例如,需要设置数据监听、编译模板、将实例挂载到 DOM 并在数据变化时更新 DOM 等。

在这个过程中会运行一些叫做生命周期钩子的函数,这给了用户在不同阶段添加自己的代码的机会

每个阶段都有两个生命周期钩子函数。

  • 创建阶段--beforeCreate,created
  • 挂载阶段--beforeMount,mounted
  • 运行阶段--beforeUpdate,updated
  • 销毁阶段--beforeDestroy,destroyed
所有的生命周期钩子自动绑定 this 上下文到实例中, 因此你可以访问数据 ,对 property 和方法进行运算。这意味着你不能使用箭头函数来定义一个生命周期方法 (例如 created: () => this.fetchTodos())。这是因为箭头函数绑定了父上下文,因此 this 与你期待的 Vue 实例不同,this.fetchTodos 的行为未定义。

下面来分别介绍一下这几个阶段 Vue 帮我们做了什么。然后我们又能在不同阶段做些什么。

创建阶段

创建阶段可以看做一个vue实例生命的开始,可以把这一阶段比作组件从受精卵到胚胎的过程,这个阶段 vue组件开始初始化,vue开始观察数据,这个阶段有 beforeCreate 和 created 两个生命周期钩子函数。

beforeCreate

beforeCreate:是new Vue()之后触发的第一个钩子,此时 data、methods、computed以及watch上的数据和方法还未初始化,都不能被访问。

在实例初始化之后,数据观测 (data observer) 和 event/watcher 事件配置之前被调用。

created

created:在实例创建完成后被立即调用,此时已完成以下的配置:数据观测 (data observer),property 和方法的运算,watch/event 事件回调。然而,挂载阶段还没开始,$el property 目前尚不可用,也就是可以使用数据,更改数据,在这里更改数据不会触发updated函数。

可以做什么:

  • data 和 methods 都已经被初始化好了,如果要调用 methods 中的方法,或者操作 data 中的数据,最早可以在这个阶段中操作。
  • 无法与Dom进行交互,如果非要想,可以通过vm.$nextTick来访问Dom。
  • 异步数据的请求适合在 created 的钩子中使用,例如数据初始化。

挂载阶段

这个阶段是vue实例的出生阶段,这个阶段将实现 DOM 的挂载,这标志着我们可以在浏览器里中看到页面了。

beforeMount

beforeMount:发生在挂载之前,在这之前 template 模板已导入渲染函数编译。此时虚拟Dom已经创建完成,即将开始渲染。在这一阶段也可以对数据进行更改,不会触发updated。

执行到这个钩子的时候,在内存中已经编译好了模板了,但是还没有挂载到页面中,此时,页面还是旧的。

mounted

mounted:在挂载完成后发生,此时真实的Dom挂载完毕,数据完成双向绑定,可以访问到Dom节点,使用$refs属性对Dom进行操作。

执行到这个钩子的时候,就表示vue实例已经初始化完成了。此时组件脱离了创建阶段,进入到了运行阶段。 如果我们想要通过插件操作页面上的DOM节点,最早可以在和这个阶段中进行。

「注意」:mounted 不会保证所有的子组件也都一起被挂载。如果你希望等到整个视图都渲染完毕,可以在 mounted 内部使用 vm.$nextTick。

mounted: function () {this.$nextTick(function () {// Code that will run only after the// entire view has been rendered})
}mounted() {this.routers = useRouter().options.routesconsole.log(this.routers)},

运行阶段

vue实例不可能一直保持不变,就像人随着年龄的增长,形体会发生变化。当vue实例中的数据发生改变时,DOM 也会发生变化。

beforeUpdate

beforeUpdate:发生在更新之前,也就是响应式数据发生更新,虚拟dom重新渲染之前被触发,你可以在当前阶段进行更改数据,不会造成重新渲染,但会再次触发当前钩子函数。

updated

updated:发生在更新完成之后,此时 Dom 已经更新。现在可以执行依赖于 DOM 的操作。然而在大多数情况下,你应该避免在此期间更改状态。如果要相应状态改变,最好使用计算属性或 watcher 取而代之。最好不要在此期间更改数据,因为这可能会导致无限循环的更新。

「注意」:updated 不会保证所有的子组件也都一起被重绘。如果你希望等到整个视图都重绘完毕,可以在 updated 里使用 vm.$nextTick。

updated: function () {this.$nextTick(function () {// Code that will run only after the// entire view has been re-rendered(代码将在整个视图重新渲染后执行)})
}

销毁阶段

vue实例的消亡阶段。实例还可以被使用,直到destroyed(),我们可以最后做一些想做的事情。

beforeDestroy

beforeDestroy:发生在实例销毁之前,在这期间实例完全可以被使用,我们可以在这时进行善后收尾工作,比如清除计时器。

Vue实例从运行阶段进入到了销毁阶段,这个时候上所有的 data 和 methods , 指令, 过滤器 ……都是处于可用状态。还没有真正被销毁。

destroyed

destroyed:发生在实例销毁之后,这个时候只剩下了dom空壳。组件已被拆解,数据绑定被卸除,事件监听器被移除,所有子实例也统统被销毁。

在大多数场景中你不应该调用这个方法。最好使用 v-if 和 v-for 指令以数据驱动的方式控制子组件的生命周期。

总结

写个例子验证一下💡,看看是否正确。

!DOCTYPE html>
<html>
<head><title></title><meta charset="UTF-8"><script type="text/javascript" src="https://cdn.jsdelivr.net/vue/2.1.3/vue.js"></script>
</head>
<body><div id="app"><p>{{ message }}</p><button @click="change()">change改变数据</button><button @click="destr()">destroy销毁实例</button><hr><button @click="getDom()">获取实例dom</button><button @click="getData()">获取实例data</button><button @click="getMethods()">获取实例methods</button>
</div><script type="text/javascript">var obj = {"el: ": this.$el, "data: ":this.data}function Life(name,el,data,methods){this['生命周期钩子函数'] = name;this['el (实例挂载的dom)'] = el;this['data (实例的数据)'] = data;this['methods (实例中的方法)'] = methods;}var app = new Vue({el: '#app',data: {message: 66},beforeCreate: function () {var obj = new Life('beforeCreate',this.$el,this.message,this.test)console.table({'创建前':obj})},created: function () {var obj = new Life('created',this.$el,this.message,this.test)console.table({'创建后':obj})},beforeMount: function () {var obj = new Life('beforeMount',this.$el,this.message,this.test)console.table({'挂载前':obj})alert('当前阶段处于 beforeMount ,此时虚拟 dom 已经创建完成,即将开始渲染。')},mounted: function () {// 实例被挂载后调用,这时 el 被新创建的 vm.$el 替换了。如果根实例挂载到了一个文档内的元素上,当 mounted 被调用时 vm.$el 也在文档内。var obj = new Life('mounted',this.$el,this.message,this.test)console.table({'挂载后':obj})},beforeUpdate: function () {// 数据更新时调用,发生在虚拟 DOM 打补丁之前。这里适合在更新之前访问现有的 DOM,比如手动移除已添加的事件监听器。var obj = new Life('beforeUpdate',this.$el,this.message,this.test)console.table({'数据更新,DOM 重新渲染前':obj})alert('当前阶段处于 beforeUpdate ,此时实例中的数据已更新,但 dom 上的数据还未更新。')},updated: function () {// 当这个钩子被调用时,组件 DOM 已经更新,所以你现在可以执行依赖于 DOM 的操作。然而在大多数情况下,你应该避免在此期间更改状态。如果要相应状态改变,通常最好使用计算属性或 watcher 取而代之。var obj = new Life('updated',this.$el,this.message,this.test)console.table({'数据更新,DOM 重新渲染后':obj})// 注意 updated 不会保证所有的子组件也都一起被重绘。如果你希望等到整个视图都重绘完毕,可以在 updated 里使用 vm.$nextTick:// 将回调延迟到下次 DOM 更新循环之后执行。在修改数据之后立即使用它,然后等待 DOM 更新。// 它跟全局方法 Vue.nextTick 一样,不同的是回调的 this 自动绑定到调用它的实例上。this.$nextTick(function () {// DOM 现在更新了// `this` 绑定到当前实例console.log('这是 update 中的回调')})},beforeDestroy: function () {this.change()// 实例销毁之前调用。在这一步,实例仍然完全可用。var obj = new Life('beforeDestroy',this.$el,this.message,this.test)console.table({'实例销毁前,此时实例还完全可用':obj})},destroyed: function () {this.message = 123// 实例销毁后调用。该钩子被调用后,对应 Vue 实例的所有指令都被解绑,所有的事件监听器被移除,所有的子实例也都被销毁。var obj = new Life('destroyed',this.$el,this.message,this.test)console.table({'实例销毁后':obj})this.test()},methods: {test() {console.log('666')},change() {this.message++;console.log(this.message)},destr() {this.$destroy();},getDom() {console.log(this.$el)},getData() {console.log(this.message)},getMethods() {console.log(this.test)}}})
</script>
</body>
</html>

效果图:

为了便于记忆,将这些内容总结成一张表格📄。

生命周期钩子函数实例处于阶段描述能否获取到el (this.$el)能否获取到data (this.xxx)能否使用methods中的方法 (this.xxx())
beforeCreate创建前实例已初始化,但数据观测,watch/event 事件回调还未配置获取不到不能不能
created创建后已完成如下配置,数据观测 (data observer),property 和方法的运算,watch/event 事件回调获取不到
beforeMount挂载前dom已初始化,但并未挂载和渲染
mounted挂载后dom已完成挂载和渲染
beforeUpdate更新前数据已改变,但dom为更新
updated更新后dom已更新
beforeDestroy销毁前实例销毁前,实例仍然可用
destroyed销毁后实例已销毁,所有指令被解绑,事件监听器被移除,子实例都被销毁

作者:邻居家的大灰狼
链接:https://juejin.cn/post/6874855535234170887
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

http://www.xdnf.cn/news/5068.html

相关文章:

  • com.fasterxml.jackson.dataformat.xml.XmlMapper把对象转换xml格式,属性放到标签<>里边
  • 部署Superset BI(四)连接sql server数据库
  • CNG汽车加气站操作工岗位职责
  • 2025医疗信息化趋势:健康管理系统如何重构智慧医院生态
  • 李沐《动手学深度学习》 | 多层感知机
  • WEB前端表单及表格标签综合案例
  • World of Warcraft [CLASSIC][80][Deluyia] [Fragment of Val‘anyr]
  • Dify智能体平台源码二次开发笔记(8)- OpenAvatarChat数字人项目+dify智能体完美融合
  • 升级 Azure Kubernetes 服务群集的关键注意事项
  • Spring Cloud LoadBalancer (负载均衡)
  • Kubernetes生产实战:NodePort端口范围的隐藏规则与调优指南
  • C——数组和函数实践:扫雷
  • 【PostgreSQL数据分析实战:从数据清洗到可视化全流程】电商数据分析案例-9.4 可视化报告输出
  • 两台服务器之前共享文件夹
  • 第十五章,SSL VPN
  • 一文了解氨基酸的分类、代谢和应用
  • Spring Web MVC基础理论和使用
  • Missashe考研日记-day36(改版说明)
  • AWS之数据分析类产品
  • 算法与数据结构 - 二叉树结构入门
  • git高效杀器——cz-customizable 搭配 commitlint
  • SAF利用由Varjo和AFormX开发的VR/XR模拟器推动作战训练
  • 【2025最新】如何定制化、高效化使用LIghtRAG进行规范知识抽取
  • STM32TIM定时中断(6)
  • 聊聊Spring AI autoconfigure模块的拆分
  • 香港科技大学(广州)新开设智能制造理学硕士学位项目线上招生宣讲会
  • 切比雪夫不等式详解
  • Vibe Coding: 优点与缺点
  • 在 Kotlin 中什么是委托属性,简要说说其使用场景和原理
  • 嵌入式openharmony标准系统中HDF框架底层原理分析