
Vue 声明周期与钩子函数
如果想要了解对于实现页面逻辑交互等效果,我们必须知道 Vue 的生命周期,知道我们写的东西应该挂载到哪里?而且说到声明周期,必定少不了钩子函数。
Vue 官方给出的 api 讲解:
所有的生命周期钩子自动绑定 this 上下文到实例中,因此你可以访问数据,对属性和方法进行运算。这意味着你不能使用箭头函数来定义一个生命周期方法(例如: created: () => this.fetchTodos()
)。这是因为箭头函数绑定了父上下文,因为 this 与你期待的 Vue 实例不同,this.fetchTodos
的行为未定义。
好像听不太懂?看下面
生命周期
生命周期函数就是 Vue 实例在某一个时间点会自动执行的函数。
简单来说就是好像把人的出生到死亡分成一个个阶段,你取名字肯定是在你出生阶段,而不是在成年阶段;你结婚肯定是在成年阶段,而不是在出生阶段;如果说你在出生阶段想去成年阶段,那肯定是不行的。
组件也是一样的,在实例化的时候特定阶段调用的特定方法,调用的这个方法就是钩子函数。
钩子函数
钩子函数和回调函数有什么区别吗?
区别:
js 派函数监听时间 → 监听函数就是所谓的钩子函数 → 函数钩取事件:函数主动找事件 → 钩子函数
js 预留函数给 dom 事件,dom 事件调用 js 预留的函数 → 事件派发给函数:事件调用函数 → 回调函数
可以简单的理解为:
钩子函数是事件被动的监听,一旦条件触发就执行
回调函数是主动事件,执行函数体内容
敲黑板
重点图来了,不说废话,附上:
是不是有那么一丁点了解了
上猛料:
Vue 中有 8 种生命周期函数:
钩子函数 | 触发的行为 | 在此阶段可以做的事情 |
---|---|---|
beforeCreate | vue 实例的挂载元素 $el 和数据对象 data 都为 undefined,还未初始化 | 加 loading 事件 |
created | vue 实例的数据对象 data 有了,$el 还没有 | 结束 loading、请求数据为 mounted 渲染做准备 |
beforeMount | vue 实例的 $el 和 data 都初始化了,但还是虚拟的 dom 节点,具体的data filter 还未替换 | |
mounted | vue 实例挂载完成,data filter 成功渲染 | 配合路由钩子使用 |
beforeUpdate | data 更新时触发 | |
updated | data 更新时触发 | 数据更新时,做一些处理(此处也可以用 watch 进行观测) |
beforeDestroy | 组件销毁时触发 | |
destroyed | 组件销毁时触发,vue 实例解除了事件监听以及和 dom 的绑定(无响应了),但 DOM 节点依旧存在 | 组件销毁时进行提示 |
就这八种
不多说了,来一组测试代码
注:此处省略部分代码
<div id="app">{{msg}}</div>
<script src="https://cdn.bootcss.com/vue/2.4.2/vue.js"></script>
<script>
var vm = new Vue({
el: '#app',
data: {
msg: 'Vue 的生命周期'
},
beforeCreate: function() {
console.group('------beforeCreate 创建前状态------');
console.log("el : " + this.$el); //undefined
console.log("data : " + this.$data); //undefined
console.log("msg: " + this.msg) //undefined
},
created: function() {
console.group('------created 创建完毕状态------');
console.log("el : " + this.$el); //undefined
console.log("data : " + this.$data); //已被初始化
console.log("msg: " + this.msg); //已被初始化
},
beforeMount: function() {
console.group('------beforeMount 挂载前状态------');
console.log(this.$el);// <div id="app">{{msg}}</div> 挂载前状态
},
mounted: function() {
console.group('------mounted 挂载结束状态------');
console.log(this.$el);// <div id="app">Vue 的生命周期</div> msg 内容被挂载并渲染到页面
},
// 当 data 被修改之前
beforeUpdate: function () {
console.group('beforeUpdate 更新前状态===============》');
console.log("el : " + this.$el);
console.log(this.$el);
console.log("data : " + this.$data);
console.log("msg: " + this.msg);
},
// 触发 beforeUpdate 之后,虚拟 DOM 重新渲染并应用更新
// 当 data 被修改之后
updated: function () {
console.group('updated 更新完成状态===============》');
console.log("el : " + this.$el);
console.log(this.$el);
console.log("data : " + this.$data);
console.log("msg: " + this.msg);
},
// 调用 vm.$destroy() 销毁前
beforeDestroy: function () {
console.group('beforeDestroy 销毁前状态===============》');
console.log("el : " + this.$el);
console.log(this.$el);
console.log("data : " + this.$data);
console.log("msg: " + this.msg);
},
// 调用 vm.$destroy() 销毁后
destroyed: function () {
console.group('destroyed 销毁完成状态===============》');
console.log("el : " + this.$el);
console.log(this.$el);
console.log("data : " + this.$data);
console.log("msg: " + this.msg)
}
})
</script>
这一大坨,肯定没有心情看... 可以直接看下面的 demo
Demo
详细说一下步骤
beforeCreate
和 created
beforeCreate:在实例初始化完成时,被执行。
一般不会使用这个钩子函数
created:在初始化结束之后会再初始化一些外部注入和一些双向绑定相关的事情时,被执行。
当前组件已构造完成,已经完成了数据劫持,把方法和计算属性都已经挂载到了当前的组件上,但是并不能获取到真实的 DOM,所以不能操作 DOM,通常会完成 Ajax 请求
这两个钩子函数执行完之后,初始化基本完成了。
在 beforeCreate 阶段,el 和 data 都没有被挂载;而在 created 阶段,el 还没被挂在,但 data 已经被挂载了,如下图所示:
疑问?这里el为啥没有被挂载呢?
看上图,在 created 执行完毕后,它会询问一个条件:你这个 Vue 实例里是否有 el 这个选项。
如果有就又会询问是否有 template 这个选项:
- 如果没有 template 就会走右侧的分支,
- 如果这个实例没有 template,就会将 el 这个根节点当做模版,来进行渲染
- 如果有 template 就会走左侧的分支
- 把 template 作为模版去渲染
beforeMount
和 mounted
beforeMount:执行时,页面还没有被渲染。
mounted:执行时,页面已经被渲染了。
真实 DOM 已完成,挂载到了页面上,也可以发起 Ajax 请求
从图中也可以看出,在 beforeMount 执行时,el 还没有被挂在;当 mounted 执行时,el 被挂载到页面了。
beforeUpdate
和 updated
beforeUpdate:数据被改变,还没渲染之前会被执行。
updated:数据被改变,渲染完成后会被执行。
不能修改 data 中的数据,可能会造成死循环
这张图中有个奇怪的现象,为什么在 beforeUpdate 和 updated 两个钩子函数中,el 和 msg 都是一样呢?beforeUpdate 执行是不应该是老数据嘛,怎么这里也是最新的数据了?
因为这里的 el 是虚拟 dom,不是真实的 dom,和 data 都是对象,在加上 console.log 这里是个异步操作,当你点开 console.log 时,其实代码早就跑完了,数据已经是最新的了,所以就会看到在这两个函数中输出结果是一样的了。
可以用 document.getElementById(‘app’).innerHTML 获取真实的 Dom 结构,这时我们就可以看到这两处不一样的地方了。
beforeDestroy
和 destroyed
调用 vm.$destroy() 方法可对实例销毁
beforeDestroy:实例被销毁前被执行。
destroyed:实例被销毁后被执行。
activated
和 deactivated
对了,也有人说这两个也是生命周期函数,那就来看看
使用 keep-alive
标签后,会有两个生命周期函数分别是:activated、deactivated
具体 keep-alive
的使用方法,请参考我的文章 《Vue 中 keep-alive 深入理解及实践总结》
activated:页面渲染的时候被执行。
deactivated:页面被隐藏或者页面即将被替换成新的页面时被执行。
当引入keep-alive
的时候,页面第一次进入,钩子的触发顺序 created → mounted → activated,退出时触发 deactivated。当再次进入(前进或者后退)时,只触发 activated。
事件挂载的方法等,只执行一次的放在 mounted 中;组件每次进去都执行的方法放在 activated 中, activated 中的方法不管是否需要缓存都会执行。
简单说一下:
- beforecreate : 可以在这加个 loading 事件,在加载实例时触发
- created : 初始化完成时的事件写在这里,如在这结束 loading 事件,异步请求也适宜在这里调用
- mounted : 挂载元素,获取到 DOM 节点
- updated : 如果对数据统一处理,在这里写上相应函数
- beforeDestroy : 可以做一个确认停止事件的确认框
- nextTick : 更新数据后立即操作 dom