每个 Vue 实例在被创建时都要经过一系列的初始化过程——例如,需要设置数据监听、编译模板、将实例挂载到 DOM 并在数据变化时更新 DOM 等。同时在这个过程中也会运行一些叫做生命周期钩子的函数,这给了用户在不同阶段添加自己的代码的机会。
上面就是Vue官方给的一张生命周期图,也许你现在看不明白,接下来我会用例子来实际讲解一下每一个生命周期钩子里面能做哪些事 不能做哪些事。
beforeCreate
在实例初始化之后,数据观测 (data observer) 和 event/watcher 事件配置之前被调用。
举个例子,在vue-cli搭建好之后,在main.js入口文件做一个测试
new Vue({
el: '#app',
data(){
return {
msg: 'test data'
}
},
methods:{
getList(){
}
},
components: { App },
template: '<App/>',
beforeCreate(){
//读取数据msg,去控制台看看,输出结果是undefined,因为此时的数据还没被绑定,此时如果调用方法也拿不到,会报一个方法不存在的错误。
console.log(this.msg)
}
})
代码在 beforeCreate 读取数据msg,去控制台看看,输出结果是undefined,因为此时的数据还没被绑定,此时如果调用方法也拿不到,会报一个方法不存在的错误
created
在实例创建完成后被立即调用。在这一步,实例已完成以下的配置:数据观测 (data observer),属性和方法的运算,watch/event 事件回调。然而,挂载阶段还没开始, $el
属性目前不可见。
new Vue({
el: '#app',
data(){
return {
msg: 'test data'
}
},
methods:{
getList(){
return 'getList'
}
},
components: { App },
template: '<App/>',
create(){
//成功拿到了数据,方法也被执行了
console.log(this.msg,this.getList())
}
同一段代码,在created钩子中就被执行了,created也是我们比较常用的声明周期钩子,在created中我们可以调用实例中的数据和方法,通常我们的异步数据请求操作会放在这个钩子里执行,也可以做一些初始化,比如加载一些缓存 等等...
beforeMount
在挂载开始之前被调用:相关的 render 函数首次被调用。
该钩子在服务器端渲染期间不被调用
我们将template模板的内容注释掉,将我们的index.html改成下面这样
<!DOCTYPE html>
<html lang="zn-Hans">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>vue-cli</title>
</head>
<body>
<div id="app">
<ul>
<li v-for="(item,index) in arr" :key="index">
{{item}}
</li>
</ul>
</div>
<!-- built files will be auto injected -->
</body>
</html>
new Vue({
el: '#app',
data(){
return {
msg: 'test data',
arr: [1,2,3]
}
},
methods:{
getList(){
return 'getList'
}
},
components: { App },
// template: '<App/>',
beforeMount(){
//此时打印li的个数为1
console.log(document.querySelectorAll('li').length)
}
我们打印出的li个数为1,因为此时DOM还没真实存在,没有渲染出来
mounted
el
被新创建的 vm.$el
替换,并挂载到实例上去之后调用该钩子。如果 root 实例挂载了一个文档内元素,当 mounted
被调用时 vm.$el
也在文档内。
<!DOCTYPE html>
<html lang="zn-Hans">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>vue-cli</title>
</head>
<body>
<div id="app">
<ul>
<li v-for="(item,index) in arr" :key="index">
{{item}}
</li>
</ul>
</div>
<!-- built files will be auto injected -->
</body>
</html>
new Vue({
el: '#app',
data(){
return {
msg: 'test data',
arr: [1,2,3]
}
},
methods:{
getList(){
return 'getList'
}
},
components: { App },
// template: '<App/>',
beforeMount(){
//此时打印li的个数为3
console.log(document.querySelectorAll('li').length)
}
此时的DOM已经渲染完毕,真是存在,个数为3。如果你有初始数据的dom渲染,在mounted中才能获取到dom
beforeUpdate
数据更新时调用,发生在虚拟 DOM 重新渲染和打补丁之前。
你可以在这个钩子中进一步地更改状态,这不会触发附加的重渲染过程。
该钩子在服务器端渲染期间不被调用。
new Vue({
el: '#app',
data(){
return {
msg: 'test data'
}
},
methods:{
getList(){
return 'getList'
}
},
components: { App },
template: '<App/>',
create(){
//成功拿到了数据,方法也被执行了
console.log(this.msg,this.getList())
}
updated
由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子。
当这个钩子被调用时,组件 DOM 已经更新,所以你现在可以执行依赖于 DOM 的操作。然而在大多数情况下,你应该避免在此期间更改状态。如果要相应状态改变,通常最好使用计算属性或 watcher 取而代之。
数据更新完毕,如果你想对数据做统一处理可以在这里
如果想分别处理不同的数据更新,可以在Vue提供的 $nextTick(callback)针对不同的变化做不同的处理
这里涉及到一个概念,异步调用队列
关于数据更新 还有一个watch,他们二者的不同在于,updated在实例中所有的数据变化都会监听到
而watch只会监听设置了的数据,
watch:{
'arr': function (){...}
}
beforeDestroy
实例销毁之前调用。在这一步,实例仍然完全可用。
该钩子在服务器端渲染期间不被调用。
destroyed
Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。
该钩子在服务器端渲染期间不被调用。
activated
keep-alive 组件激活时调用。
该钩子在服务器端渲染期间不被调用。
deactivated
keep-alive 组件停用时调用。
该钩子在服务器端渲染期间不被调用。