Vue的生命周期/钩子函数
什么是生命周期
Vue实例有一个完整的生命周期,也就是从开始创建、初始化数据、编译模板、挂载DOM、渲染---->更新---->渲染、卸载等一系列过程,称为Vue的生命周期。通俗说就是Vue实例从创建到销毁的过程,就是生命周期。
所有的生命周期钩子函数自动绑定’this’到实例中, 因此可以根据this访问数据,对属性和方法进行运算,也就是说Vue的整个生命周期中提供的一系列事件中的this指向的就是Vue实例。同时意味着不能使用箭头函数来定义一个声明周期方法,因为箭头函数中的’this’,是跟父级有绑定的,所以箭头函数中的’this’与钩子函数中的this不同;
l例如:
正确方式:
created() { // 钩子函数只能用函数体方式定义 }
错误方式:
created => { // 用箭头函数的方式定义钩子函数是错误的, 此方式不可取; }
Vue中生命周期分为初始化、更新、销毁三个阶段;
- 初始化: beforeCreate、created、beforeMount、mounted;
- 更新: beforeUpdate、updated;
- 销毁: beforeDestory、destoryed;
其中created和mounted可以用来发送ajax请求, 启动定时器等异步任务;
beforeDestory用来做收尾工作,eg:清除定时器;
new Vue实例
此时只是初始化下Vue实例,初始化事件系统,初始化声明周期函数;
beforeCreate
调用时机: 在实例初始化之后,数据观测(data observer) 和 event/watcher 事件配置之前被调用(也就是说data、watcher、methods都还没有,但是$route对象是存在的);
详情: 根据组件中data中声明的数据, 把它们每一个数据都进行getter / setter劫持,赋值到this组件实例上(也就是说data上所有的数据都绑定到this上), 同时处理依赖data数据的watch、computed等数据;
完成的项: 触发beforeCreate时,仅仅是初始化的准备工作做好了, 完成了组件的实例化,事件、钩子函数声明都初始化好了;
created
调用时机: 在实例创建完成后被立即调用。
详情: 1. 在这一步,实例已完成以下的配置:数据观测(data observer),属性和方法的运算,watch / event事件回调。然而,挂载阶段还没开始,$el属性目前不可见。
2. 根据传入的配置项rende(函数)r>template>el(el即声明了模板也声明了挂载点, el指将来dom元素挂载到什么地方), 找到模板,准备解析,但是还没有开始挂载;
完成的项: 此时数据处理完成完成了组件的数据劫持,组件中的data、watch、computed通过路由传递的参数等数据都已经准备好了;
能做什么: 此时可以使用这些数据发起ajax请求;
new Vue({
data: {
a: 1
},
created: function () {
// 'this'指向 vm 实例
console.log('a is:' + this.a)
}
})
// 打印结果: a is: 1
beforeMount
调用时机: 在挂载开始之前被调用: 相关的 render 函数首次被调用(此时模板还没有被解析出来);
详情: 该钩子在服务器渲染期间不被调用;
完成的项: 完成了模板的查找工作(但模板并没有解析只是找到了);
能做什么: 解析模板, 解析其中的指令, 替换其中的数据变量, 绑定事件, 渲染成一个浏览器认识的dom字符串,挂载到指定的节点上;
mounted
调用时机: el 被新创建的 vm.’&'el 替换, 并挂载到实例上去之后调用该钩子函数(vm是ViewModel的缩写,vm变量名表示Vue实例; Vue上自身的属性和方法都带有¥的英文符号,用于和开发者自己定义的属性与方法区分开);
详情:
完成的项: 已经完成了模板的解析(指令解析、事件绑定、数据的替换),生成了浏览器真正识别的dom字符串,并挂载到el声明的挂载点上;
能做什么: 可以获取模板中元素的dom节点; 执行了某个业务逻辑(增删改),更新数据;
注意:
mounted不会承诺所有的子组件也都一起被挂载。如果希望等到整个视图都渲染完毕,可以用vm.$nextTick替换掉mounted,如:
mounted: function () {
this.$nextTick(function () {
// **该钩子函数在服务器端渲染期间不被调用**;
})
}
beforeUpdate
调用时机: 数据更新时调用,发生在虚拟DOM打补丁之前;
详情: 这里是个在更新之前访问现有的DOM,eg:手动移除已添加的事件监听器;
完成的项: 此时数据是新的;
能做什么: 使用新的数据更新视图;页面响应式数据发生变化后触发该钩子函数,此时数据已经是新的,页面的内容还是旧的。
注意: 该钩子函数在服务器端渲染期间不被调用,因为只有初次渲染会在服务端进行; 此时数据是新的,页面显示还是旧的数据;
updated
调用时机: 由于数据更改导致的虚拟DOM重新渲染和打补丁,在这之后调用该钩子函数;
详情: 这个钩子函数被调用时,组件DOM已经更新,所以现在可以执行依赖于DOM的操作。然而在大多数情况下,应该避免在此期间更改状态,因为这可能会导致更新无限循环。如果要相应状态改变,通常最好使用计算属性或watcher取而代之;
完成的项: 此时数据是新的,视图显示也是新的;
能做什么: 响应式数据更新后引起dom更新,dom更新后触发该钩子函数;
注意: 1.当我们离开这个页面时,页面中的组件都即将被销毁, 先触发beforeDestory: 执行以下清理(不使用的数据、定时任务等)工作;
2… updated不会承诺所有的子组件也都一起被重绘。如果希望等到整个视图都重绘完毕,可以用Vm.$nextTick替换掉updated, 如:
updated: function () {
this.$nextTick(function () {
**该钩子在服务器端渲染期间不被调用**
})
}
activated
调用时机: 当使用 Keep-alive组件包裹的组件再次被显示使用时触发该钩子函数;
详情:
完成的项:
能做什么:
注意: 该钩子在服务器端渲染期间不被调用
deactivated
调用时机: 当使用keep-alive组件包裹的组件被隐藏时触发该钩子函数;
详情:
完成的项:
能做什么:
注意: 该钩子在服务器端渲染期间不被调用
beforeDestroy
调用时机: 实例销毁之前调用;
详情:
完成的项:
能做什么: 即将离开这个组件(非缓存组件)不再使用时,触发该钩子函数,在这里可以执行一些数据/定时器清理工作;
注意: 该钩子在服务器端渲染期间不被调用,在这一步实例仍然可用。
destroyed
调用时机: Vue实例销毁之后调用;
详情: 调用后, Vue实例指示的所有东西都会解绑定, 所有的事件监听器都会被移除,所有的子实例也会被销毁。
完成的项:
能做什么: 当组件销毁后触发该钩子函数,此时组件的响应式数据、侦听器、计算属性、绑定的事件都不再有效;
注意: 该钩子在服务器端渲染期间不被调用
代码验证
<body>
<div id="test">
<!-- 点击按钮后触发destoryVue事件 -->
<button @click="destoryVue">destory vue</button>
<p v-if="isShow">你好</p>
</div>
<script type="text/javascript" src="../js/vue.js"></script>
<script type="text/javascript">
new Vue ({
el: '#test',
data: {
isShow: true
},
beforeCreate () {
// 初始化Vue实例后触发;
// 此时仅仅是初始化的准备工作做好了;
console.log('beforeCreate()')
console.log('beforeCreate 页面数据: ' this.data)
console.log('beforeCreate 页面节点: ' this.$el)
},
created () {
// 实例创建完成后被立即调用;
// 此时可以发起ajax请求;
console.log('created()')
},
beforeMount () {
// 挂载开始之前被调用;
// 开始解析模板;
console.log('beforeMount()')
},
mounted () {
// 挂载之后被调用;
// 可以获取模板中元素的dom节点,已更新了数据;
console.log('mounted()')
// 执行异步任务
this.intervalId = setInterval(() => {
console.log('------')
this.isShow = !this.isShow //isShow值为false时, P标签不显示
}, 1000)
},
beforeUpdate () {
// 数据更新时调用, 在虚拟DOM打补丁之前;
// 此时数据是新的,但页面显示的数据还是旧的;
console.log('beforeUpdate()')
},
updated () {
// 虚拟DOM重新渲染和打补丁之后被调用;
// 此时数据和页面数据都是新的;
console.log('updated()')
},
beforeDestroy () {
// 实例销毁之前调用;
// 此时实例仍然可用;
console.log('beforeDestory()')
// 执行收尾的工作
clearInterval(this.intervalId)
},
destroyed () {
// Vue实例销毁之后调用;
console.log('destoryed()')
},
methods: {
destoryVue () {
// 点击按钮后执行destroy()事件,开始销毁
this.$destroy() // 触发beforeDestory 和 destoryed的例子
}
}
})
/*
执行结果:
beforeCreate()
created()
beforeMount()
mounted()
vue.js:8553 You are running Vue in development mode.
Make sure to turn on production mode when deploying for production.
See more tips at https://vuejs.org/guide/deployment.html
------
beforeUpdate()
updated()
------
beforeUpdate()
updated()
------
beforeUpdate()
updated()
beforeDestory()
destoryed()
*/
</script>
</body>
父子组件的生命周期执行顺序
- 组件嵌套2层(父子组件<无第三层>):
-
全流程:
父组件beforeCreate --> 父组件created --> 父组件beforeMount --> 子组件beforeCreate --> 子组件created --> 子组件beforeMount --> 子组件mounted --> 父组件mounted --> 父组件beforeUpdate --> 子组件beforeUpdate --> 子组件updated --> 父组件updated --> 父组件beforeDestroy --> 子组件beforeDestroy --> 子组件destroy --> 父组件destroy -
加载渲染过程:
父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount->子mounted->父mounted -
子组件更新过程:
父beforeUpdate->子beforeUpdate->子updated->父updated -
父组件更新过程:
父beforeUpdate->父updated -
销毁过程:
父beforeDestroy->子beforeDestroy->子destroyed->父destroyed