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.listsconsole.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'))//nullconsole.log('data:',this.text);//undefinedthis.sayHello();//error:not a function},created(){console.log('create:',document.getElementById('first'))//nullconsole.log('data:',this.text);//this.textthis.sayHello();//this.sayHello()},beforeMount(){console.log('beforeMount:',document.getElementById('first'))//nullconsole.log('data:',this.text);//this.textthis.sayHello();//this.sayHello()},mounted(){console.log('mounted:',document.getElementById('first'))//<p></p>console.log('data:',this.text);//this.textthis.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,依旧能够在浏览器中看到整体的页面)。
