1.生命周期的基本概念
每个 Vue 实例在被创建时都要经过一系列的初始化过程——例如,需要设置数据监听、编译模板、将实例挂载到 DOM 并在数据变化时更新 DOM 等。同时在这个过程中也会运行一些叫做生命周期钩子的函数,这给了用户在不同阶段添加自己的代码的机会。
1.beforeCreate
在这个钩子函数里,只是刚开始初始化实例,你拿不到实例里的任何东西,比如data和methods和事件监听等。
data: {
msg: 'linlin'
},
methods: {
getLists(){
return 'aaa'
}
},
beforeCreate() {
console.log('beforeCreate',this.msg,this.getLists())
}
运行上面代码,控制台报错
因为就像前面说的这个钩子函数里拿不到data和methods
2.created
在实例创建完成后被立即调用。在这一步,实例已完成以下的配置:数据观测 (data observer),属性和方法的运算,watch/event 事件回调。然而,挂载阶段还没开始,$el
属性目前不可见。
这是最早能拿到实例里面的数据和方法的一个钩子函数。应用场景:异步数据的获取和对实例数据的初始化操作都在这里面进行。
data: {
msg: 'linlin',
imgs: null
},
methods: {
getLists(){
this.$http.get(url).then(res=>{
this.imgs = res.data.lists
console.log(this.imgs)
})
}
},
created() {
this.getLists()
}
3.beforeMount
在挂载开始之前被调用:相关的render
函数首次被调用。
不论是created还是beforeMount在它们里面都拿不到真实的dom元素,如果我们需要拿到dom元素就需要在mounted里操作
<div id="app">
<ul>
<li v-for="(item,index) in arr" :key="index">{{item}}</li>
</ul>
</div>
<script>
let app = new Vue({
data: {
arr: [1,2,3]
},
created() {
console.log('created',document.querySelectorAll('li').length)
},
beforeMount() {
console.log('beforeMount',document.querySelectorAll('li').length)
},
mounted() {
console.log('mounted',document.querySelectorAll('li').length)
},
})
</script>
4.mounted
上面的案例mounted可以拿到dom元素,但也只是能拿到初始化数据里的dom元素,如果是存在异步对dom元素数据进行更改我们就只能在updated里获取,应用场景:初始数据(在data中有的)的dom渲染完毕,可以获取dom
<div id="app">
<ul>
<li v-for="(item,index) in arr" :key="index">{{item}}</li>
</ul>
</div>
created() {
setTimeout(()=>{
this.arr = [4,5,6,7]
console.log('created',document.querySelectorAll('li').length)
})
},
mounted() {
console.log('mounted',document.querySelectorAll('li').length)
}
执行上面代码,发现不管是mounted还是created里拿到的都是3,而不是4
这里之所以先打印出mounted是因为created里面的是异步操作所以最后才执行完,而我们如果想要拿到正确的dom元素的个数就需要在updated里面获取
updated(){
console.log('updated',document.querySelectorAll('li').length)
}
另外$children子组件的获取也需要在mounted里
5.beforeUpdate
当数据更新后出发的钩子函数,这个钩子函数里拿到的是更改之前的数据,虚拟DOM重新渲染和打补丁之前被调用。
你可以在这个钩子中进一步地修改data,这不会触发附加的重渲染过程。
6.updated
上面虽然能在update里拿到更改后的数据,但是并不建议在这里面进行对异步数据得到的dom操作,因为有可能你当前的数据不止更改一次,而update只要相关的数据更改一次就会执行一次,注意:updated是指mouted钩子后(包括mounted)的数据更改,在created里的数据更改不叫更改叫做初始化,所以我们下面在created里修改数据是通过一个异步来确保updated可以执行的。我们一般都是在事件方法里更改数据,然后通过updated对其操作。应用场景:如果dom操作依赖的数据是在异步操作中获取,并且只有一次数据的更改 ,也可以说是数据更新完毕:如果对数据更新做一些统一处理在updated钩子中处理即可。
注意:当这个钩子被调用时,组件DOM的data已经更新,所以你现在可以执行依赖于DOM的操作。但是不要在当前钩子里修改当前组件中的data,否则会继续触发beforeUpdate、updated这两个生命周期,进入死循环!
created() {
setTimeout(()=>{
this.arr = [4,5,6,7]
console.log('created',document.querySelectorAll('li').length)
})
setTimeout(()=>{
this.arr = [10,11,12,13,14]
console.log('created',document.querySelectorAll('li').length)
},1000)
},
beforeUpdate() {
console.log('beforeUpdate',document.querySelectorAll('li').length)
},
updated() {
console.log('updated',document.querySelectorAll('li').length)
},
上面updated执行了两遍,之所以一开始created是3是因为上面我们说的他是在一个异步里,也是在mouted后获取的所以是3。
在事件方法里更改数据
<div id="app">
<ul>
<li v-for="(item,index) in arr" :key="index" @click="getAdd">{{item}}</li>
</ul>
<div>{{msg}}</div>
</div>
data: {
arr: [1,2,3],
},
methods: {
getAdd(){
this.arr = [4,5,6,7]
}
},
updated() {
console.log(this.arr)
}
如果想分别区别不同的数据更新,同时要对dom进行操作那么需要使用nextTick函数处理
created() {
setTimeout(()=>{
this.arr = [4,5,6,7]
this.$nextTick(()=>{
console.log('nextTick',document.querySelectorAll('li').length)
})
})
setTimeout(()=>{
this.arr = [10,11,12,13,14]
this.$nextTick(()=>{
console.log('nextTick',document.querySelectorAll('li').length)
})
},1000)
},
这样我们想对哪一次的数据进行操作就直接在this.$nextTick里面写就行
在updated中修改数据陷入死循环
<div id="app">
{{a}}
<button @click="a=2">点我</button>
</div>
<script>
new Vue({
el: '#app',
data: {
a: [1]
},
beforeUpdate() {
console.log('beforeUpdate')
},
updated() {
console.log(this.a)
console.log('updated')
this.a = [2]
},
})
</script>
updated,watch和nextTick区别
updated对所有数据的变化进行统一处理
watch对具体某个数据变化做统一处理
nextTick是对某个数据的某一次变化进行处理
如果实例里面没写el挂载点你就需要在实例后面通过.$mount(‘#app’)来手动触发
7.beforeDestroy
实例销毁之前调用。在这一步,实例仍然完全可用
8.destroyed
Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。
beforeDestroy和destroyed只能通过手动触发$destroy来调用
let app = new Vue({
beforeDestroy() {
console.log('beforeDestroy')
},
destroyed() {
console.log('destroyed')
}
})
app.$destroy()
2.vue中created与mounted的区别
关于vue.js中的生命周期,如果不是有特别的需求,一般在项目开发过程中更多的使用created和mounted,
所以在本文中主要讲解created与mounted在开发中的主要使用区别。
关于完整的生命周期,不久会在另一篇文章中做整体的理解,包括activated、destroyed等,不过可能会有点晚,大家可以留意一下
1.生命周期
完整的生命周期图示为了避免占用板块,这里就不贴出来了,大家可以自行前往vue生命周期查看。
2.浏览器渲染过程
- 构建DOM树
- 构建css规则树,根据执行顺序解析js文件。
- 构建渲染树Render Tree
- 渲染树布局layout
- 渲染树绘制
3.生命周期中的浏览器渲染
beforeCreate(){
console.log('beforecreate:',document.getElementById('first'))//null
console.log('data:',this.text);//undefined
this.sayHello();//error:not a function
},
created(){
console.log('create:',document.getElementById('first'))//null
console.log('data:',this.text);//this.text
this.sayHello();//this.sayHello()
},
beforeMount(){
console.log('beforeMount:',document.getElementById('first'))//null
console.log('data:',this.text);//this.text
this.sayHello();//this.sayHello()
},
mounted(){
console.log('mounted:',document.getElementById('first'))//<p></p>
console.log('data:',this.text);//this.text
this.sayHello();//this.sayHello()
}
通过上述测试我们可以知道,
生命周期 | 是否获取dom节点 | 是否可以获取data | 是否获取methods |
---|---|---|---|
beforeCreate | 否 | 否 | 否 |
created | 否 | 是 | 是 |
beforeMount | 否 | 是 | 是 |
mounted | 是 | 是 | 是 |
以我的个人理解,vue生命周期实际上和浏览器渲染过程是挂钩的,
在beforecreate阶段,对浏览器来说,整个渲染流程尚未开始或者说准备开始,对vue来说,实例尚未被初始化,data observer和 event/watcher也还未被调用,在此阶段,对data、methods或文档节点的调用现在无法得到正确的数据。
在created阶段,对浏览器来说,渲染整个HTML文档时,dom节点、css规则树与js文件被解析后,但是没有进入被浏览器render过程,上述资源是尚未挂载在页面上,也就是在vue生命周期中对应的created
阶段,实例已经被初始化,但是还没有挂载至$el上,所以我们无法获取到对应的节点,但是此时我们是可以获取到vue中data与methods中的数据的
在beforeMount阶段,实际上与created阶段类似,节点尚未挂载,但是依旧可以获取到data与methods中的数据。
在mounted阶段,对浏览器来说,已经完成了dom与css规则树的render,并完成对render tree进行了布局,而浏览器收到这一指令,调用渲染器的paint()在屏幕上显示,而对于vue来说,在mounted阶段,vue的template成功挂载在$el中,此时一个完整的页面已经能够显示在浏览器中,所以在这个阶段,即可以调用节点了(关于这一点,在笔者测试中,在mounted方法中打断点然后run,依旧能够在浏览器中看到整体的页面)。