Vue生命周期的理解

作为前端三大框架之一的 Vue

在前段时间 GitHub Star 超越了 react,成为各大社区讨论的热点,以前有过简单了解,今天和同事在开发 Vue 项目时遇到了
问题(用 ajax 请求数据,然后将数据 props 到子组件的时候,因为 ajax 是异步的,然后会发生没有数据),就来更好的学习了解
下 Vue 的生命周期,了解使用过 react,再回头看 Vue,发现很熟悉啊,

Vue 实例

  • 实例化 Vue,需要注意的是含 this 的函数大多不要使用箭头函数,因为我们期望 this 指向 Vue 实例
  • data Vue 实例的数据都保存在 data 对象中,Vue 将会递归将 data 的属性转换为 getter/setter,
    从而让 data 的属性能够响应数据变化
1
2
3
4
5
6
7
8
const app = new Vue({
// 直接创建一个实例
data: {
a: 1
}
})
})
<!-- 这样数据就绑定在 HTML 中,Vue 框架监视 data 的数据变化,自动更新 HTML 内容 -->

生命钩子函数,它可以总共分为 11 个阶段:
注意:Vue 的所有生命周期函数都是自动绑定到 this 的上下文上。所以,你这里使用箭头函数的话,就会出现 this 指向的父级作用域,就会报错。

beforeCreate(创建前)

  • 在实例初始化之后,数据观测 (data observer) 和 event/watcher 事件配置之前被调用
  • 不能访问到 methods、data、computed、watch 上的方法和数据
1
2
3
el: undefined;
data: undefined;
message: undefined;

created(创建后)

  • 在实例创建完成后被立即调用。在这一步,实例已完成以下的配置:数据观测 (data observer),属性和方法的运算,watch/event 事件回调。
  • 组件实例化完成,属性已绑定,但 DOM 还未生成,不能访问到$el属性,$ref 属性内容为空数组
  • 此时可以调用 methods 中定义的方法,修改 data 的数据,并且可触发响应式变化、computed 值重新计算,watch 到变更等
  • 这个生命周期阶段比较常用,比如 ajax 请求获取初始化数据对实例进行初始化预处理等操作
1
2
3
el     : undefined
data : [object Object]
message: Vue的生命周期

beforeMount(载入前)

  • beforeMount 之前,会找到对应的 template,并编译成 render 函数

    template 查找的优先级顺序:
    template 参数 > el 外部 HTML
    如果指定了 render 函数,则直接采用 render 函数,即忽略 template 参数和 el 外部 HTML

  • 在挂载开始之前被调用。相关的 render 函数首次被调用。实例已完成以下的配置:编译模板,把 data 里面的数据和模板生成 html。
  • 注意此时还没有挂载 html 到页面上。
1
2
3
el     : [object HTMLDivElement]
data : [object Object]
message: Vue的生命周期

mounted(载入后)

  • 实例挂载到 DOM 上,此时可以通过 DOM API 获取到 DOM 节点,$ref 属性可以访问
  • 注意 mounted 只会执行一次
  • 注意 mounted 不会承诺所有的子组件也都一起被挂载。如果你希望等到整个视图都渲染完毕,可以用 vm.$nextTick 替换掉 mounted
1
2
3
el     : [object HTMLDivElement]
data : [object Object]
message: Vue的生命周期

beforeUpdate(更新前)

  • 这里的更新对象是模板,即需要虚拟 DOM 重新渲染和打补丁,beforeUpdate 发生在以上两个流程之前,此时新的虚拟 DOM 已经生成
  • 如果对状态进行变更会导致重新进入 beforeUpdate(这里变更的状态同样要在模板中使用,如果变更没有在模板中使用的 data,才不会再次触发重渲染流程)而且若变更操作不是基础类型的简单赋值,还会引起死循环(不断重新进入 beforeUpdate)!
  • 应该避免在这个钩子函数中操作数据

updated(更新后)

  • 由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子
  • 当这个钩子被调用时,组件 DOM 已经更新,可以执行依赖于 DOM 的操作
  • 同样,应该避免在这个钩子函数中操作数据

activated(keep-alive 组件激活时调用)


deactivated(keep-alive 组件停用时调用)


beforeDestroy(销毁前)

  • 实例销毁之前调用。在这一步,实例仍然完全可用,this 仍能获取到实例
  • 一般在这一步中进行:销毁定时器、解绑全局事件、销毁插件对象等操作

destroyed(销毁后)

  • Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁

errorCaptured(2.5+新增)

  • 捕获一个来自子孙组件的错误时被调用
  • 当捕获一个来自子孙组件的错误时被调用。此钩子会收到三个参数:错误对象、发生错误的组件实例以及一个包含错误来源信息的字符串。此钩子可以返回 false 以阻止该错误继续向上传播。
  • 你可以在此钩子中修改组件的状态。因此在模板或渲染函数中设置其它内容的短路条件非常重要,它可以防止当一个错误被捕获时该组件进入一个无限的渲染循环。

Vue 生命周期图

image.jpg

测试学习检测使用代码(摘自网络)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>vue生命周期学习</title>
<script src="https://cdn.bootcss.com/vue/2.4.2/vue.js"></script>
</head>
<body>
<div id="app">
<h1>{{message}}</h1>
</div>
</body>
<script>
var vm = new Vue({
el: "#app",
data: {
message: "Vue的生命周期",
},
beforeCreate: function () {
console.group("------beforeCreate创建前状态------");
console.log("%c%s", "color:red", "el : " + this.$el); //undefined
console.log("%c%s", "color:red", "data : " + this.$data); //undefined
console.log("%c%s", "color:red", "message: " + this.message);
},
created: function () {
console.group("------created创建完毕状态------");
console.log("%c%s", "color:red", "el : " + this.$el); //undefined
console.log("%c%s", "color:red", "data : " + this.$data); //已被初始化
console.log("%c%s", "color:red", "message: " + this.message); //已被初始化
},
beforeMount: function () {
console.group("------beforeMount挂载前状态------");
console.log("%c%s", "color:red", "el : " + this.$el); //已被初始化
console.log(this.$el);
console.log("%c%s", "color:red", "data : " + this.$data); //已被初始化
console.log("%c%s", "color:red", "message: " + this.message); //已被初始化
},
mounted: function () {
console.group("------mounted 挂载结束状态------");
console.log("%c%s", "color:red", "el : " + this.$el); //已被初始化
console.log(this.$el);
console.log("%c%s", "color:red", "data : " + this.$data); //已被初始化
console.log("%c%s", "color:red", "message: " + this.message); //已被初始化
},
beforeUpdate: function () {
console.group("beforeUpdate 更新前状态===============》");
console.log("%c%s", "color:red", "el : " + this.$el);
console.log(this.$el);
console.log("%c%s", "color:red", "data : " + this.$data);
console.log("%c%s", "color:red", "message: " + this.message);
},
updated: function () {
console.group("updated 更新完成状态===============》");
console.log("%c%s", "color:red", "el : " + this.$el);
console.log(this.$el);
console.log("%c%s", "color:red", "data : " + this.$data);
console.log("%c%s", "color:red", "message: " + this.message);
},
beforeDestroy: function () {
console.group("beforeDestroy 销毁前状态===============》");
console.log("%c%s", "color:red", "el : " + this.$el);
console.log(this.$el);
console.log("%c%s", "color:red", "data : " + this.$data);
console.log("%c%s", "color:red", "message: " + this.message);
},
destroyed: function () {
console.group("destroyed 销毁完成状态===============》");
console.log("%c%s", "color:red", "el : " + this.$el);
console.log(this.$el);
console.log("%c%s", "color:red", "data : " + this.$data);
console.log("%c%s", "color:red", "message: " + this.message);
},
});
</script>
</html>