什么是vue的生命周期:在我们创建一个vue实例的时候vue内部是会做很多事情的,这个实例从创建到运行再到销毁,会触发vue里的一系列事件,这些事件就是vue的生命周期;vue的生命周期大致分为四个阶段:创建阶段,挂载阶段,运行阶段,销毁阶段;以下是生命周期示意图可供参考:
beforeCreate:实例被创建之前;在我们创建一个vue实例的时候,vue内部首先初始化实例的所有默认事件和生命周期函数,之后就有了第一个生命周期函数beforeCreate;在这个阶段的函数拿不到实例的任何数据;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
let myVue = new Vue({ el:'#mydiv', data:{ name:'lele', age:23, job:'coder' }, methods:{ myclick(){ console.log('myclick') } }, beforeCreate(){ console.log(this)//这个给时候实力已经被创建可以访问:Vue{...} console.log(this.name)//undefined console.log(this.myclick)//undefoned }) |
created:实例被创建之后;实例在经历完beforeCreate之后,会继续初始化实例里的data数据和methods里的方法,并将data里的每一个属性都添加上getter和setter便于后期观测做到数据响应式(数据观测);这个阶段可以访问到vue里的一些数据,可以发送异步请求(但是注意这里边异步请求时间长了容易造成白屏现象),但是挂载阶段还没开始,$el属性是不可访问的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
let myVue = new Vue({ el:'#mydiv', data:{ name:'lele', age:23, job:'coder' }, methods:{ myclick(){ console.log('myclick') } }, created(){ console.log(this)//Vue{...} console.log(this.name)//这个时候已经初始化data里的数据可以访问:lele console.log(this.myclick)//这个时候已经可以访问methods里的属性:myclick(){...} console.log(this.$el)//undefined } }) |
在实例挂载之前还有一个模板编译阶段,1、在这个阶段vue会判断当前实例是否有el选项,如果有则继续判断是否有template属性;如果没有el选项,那么vue的生命周期到就会停止,只有当手动调用vm.$mount(el)
时生命周期才继续运行;2、然后再判断当前实例是否有template选项,如果有就将template作为模板编译成渲染函数,如果没有template就将el的外部HTML作为template进行编译。当同时存在template和el时,当然是将template作为模板进行编译;vue其实还有render选项,当render选项存在时他们的优先级是:render > template > el外部HTML
模板编译结束之后就来到了实例挂载的阶段
beforeMount:实例挂载之前;在完成模板编译之后就会触发beforeMount生命周期函数,在这个阶段虚拟DOM已经被渲染可以访问到$el,但是还没有挂载到对应的真实DOM上所以还访问不到$refs,真是DOM上的{{}}数据也是没有的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
let myVue = new Vue({ el:'#mydiv', data:{ name:'lele', age:23, job:'coder' }, methods:{ myclick(){ console.log('myclick') } }, beforeMount(){ console.log(this)//Vue{...} console.log(this.name)//lele console.log(this.myclick)//myclick(){...} console.log(this.$el)//这个时候虚拟DOM已经存在了,可以访问到$el:<div id='mydiv'.....</div> console.log(this.$refs)//因为还没有挂载到真是DOM上所以$refs为空:{} } }) |
mounted:实例挂载之后;在beforeMounted之后,vue用渲染好的vm.$el替换原先的el,这个时候实例已经挂载在DOM上了(此时的DOM是vue渲染的DOM),可以通过$refs正常访问这个DOM,这个DOM里的{{}}数据也被替换
带有子组件的实例的生命周期:(父)beforeCreate---(父)created---(父)beforeMount---(子)beforeCreate---(子)created---(子)beforeMount---(子)mounted---(父)mounted---(父)beforeUpdate--(子)beforeUpdate---(子)updated---(父)updated---(父)beforeDestory---(子)beforeDestory---(子)destoryed---(父)destoryed ;当有混入组件mixin的时候mixin组件的所有周期函数要优先执行;注意mounted不承诺所有组件全都挂载完成,如果想要确保整个视图渲染完毕需要使用vm.nextTick
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
<div id="mydiv" ref='mydivref'> </div> /////////////////////////////////////////////////////////////////////////////// let myVue = new Vue({ el:'#mydiv', data:{ name:'lele', age:23, job:'coder' }, methods:{ myclick(){ console.log('myclick') } }, mounted(){ console.log(this)//Vue{...} console.log(this.name)//lele console.log(this.myclick)//myclick(){...} console.log(this.$el)//<div id='mydiv'.....</div> console.log(this.$refs)//这个时候虚拟DOM已挂载可以访问$refs:{mydivref: div#mydiv} } }) |
在完成挂载之后就进行到了运行阶段,在有数据发生该变的时候,就会触发update阶段将虚拟DOM重新渲染并更新到页面DOM;注意这里只有改变写在模板里的数据才会触发这个阶段 ,改变没有写在模板里的数据是不会触发数据更新阶段的
beforeUpdate:数据更新前,更准确的说是DOM更新前;;在重新渲染虚拟DOM之前,vue给出一个beforeUpdate钩子函数。可以用来操作更新前的DOM;注意:在beforeUpadte中打印没有改变的原DOM,因为不管是$el还是$refs都是对象,在控制台打印的时候updated已经触发了,这个时候打印的$el和$refs都是updated改变后的对象了(其实beforeUpdate中访问的$el就是原来的那个DOM),我们可以在打印的时候加上innerHTML或者outerHTML就可以看到打印之前的DOM了;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
//!!!改变未在模板中使用的数据无法触发beforeUpdate和updated <div id="mydiv" ref='mydivref' @click='myclick'> <h1>{{name}}</h1> </div> /////////////////////////////////////////////////////////////////////////////// let myVue = new Vue({ el:'#mydiv', data:{ name:'lele', age:23, job:'coder' }, methods:{ myclick(){ console.log('myclick'); this.age = 99;//点击的时候改变age属性值,这个属性并没有写在模板中,所以是无法触发更新阶段的钩子函数 } }, beforeUpdate(){ console.log('beforeUpdate')//无法触发数据更新阶段 } }) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
<div id="mydiv" ref='mydivref' @click='myclick'> <h1>{{name}}</h1> </div> /////////////////////////////////////////////////////////////////////////////// let myVue = new Vue({ el:'#mydiv', data:{ name:'lele', age:23, job:'coder' }, methods:{ myclick(){ console.log('myclick'); this.name = '乐乐'; this.age = 99; } }, beforeUpdate(){ console.log(this.$el.outerHTML)//这里打印的是:<div id="mydiv"><h1>lele</h1></div> } }) |
updated:DOM更新完之后;在经历了重新渲染虚拟DOM并更新应用到页面之后就来到了vue的updated阶段,这个阶段的DOM就是已经更新过的DOM;注意:updated也不保证所有的子组件都已经重绘,如果需要等到整个视图更新完毕再做操作可以使用vm.nextTick(function(){})
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
<div id="mydiv" ref='mydivref' @click='myclick'> <h1>{{name}}</h1> </div> /////////////////////////////////////////////////////////////////////////////// let myVue = new Vue({ el:'#mydiv', data:{ name:'lele', age:23, job:'coder' }, methods:{ myclick(){ console.log('myclick'); this.name = '乐乐'; this.age = 99; } }, updated(){ console.log(this.$el)//这里打印的是:<div id="mydiv"><h1>乐乐</h1></div> } }) |
在有keep-alive导航守卫的时候组件还会额外触发以下生命周期函数
activetad:keep-alive组件激活的时候触发该函数
deactivated:keep-alive组件销毁的时候触发该函数
有路由守卫的生命周期和路由守卫的触发顺序:
beforeEach
beforeResolve
beforeRouteEnter
,在生命周期钩子之前beforeCreate
,组件实例刚被创建,属性生效之前created
,实例已经创建完成,属性已绑定,但是DOM还未生成,适合初次获取数据beforeRouteLeave
,导航离开当前路由前触发beforeMount
,挂载之前,render函数或template编译成虚拟DOM之后mounted
,挂载完成,真实DOM挂载在页面后调用,适合初始化数据beforeRouteUpdate
,当前路由更新时调用activated
,keep-alive组件激活时调用beforeUpdate
,数据更新时调用,发生在虚拟DOM重新渲染之前updated
,数据更新后调用deactivated
,keep-alive组件停用时调用afterEach
beforeDestroy
,实例销毁前调用,适合移除事件监听、定时器等destroyed
,实例销毁后调用
1 |
beforeDestor:实例销毁之前调用;在我们销毁当前实例之前所作的事情可以写在这里,这个时候实例还在this还可以访问,一般会在这个阶段销毁定时器、解绑全局事件、销毁插件对象等的善后操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
let myVue = new Vue({ el:'#mydiv', data:{ name:'lele', age:23, job:'coder' }, timmer(){ return setInterval(()=>console.log(666),1000) }, methods:{ myclick(){ console.log('myclick'); this.name = '乐乐'; this.age = 99; } }, beforeDestory(){ console.log('beforeDestory') console.log(this)//可以访问this clearInterval(this.$options.timmer)//清除全局定时器 } }) |
destroyed:实例销毁之后调用;这个时候实例已经被销毁,所有的东西都会解绑this无法访问,所有子实例也都被销毁; 它只是完全销毁一个vue实例,清理它与其他实例的链接,解绑它所有的指令和事件监听器,并不会清除页面上的DOM
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
let myVue = new Vue({ el:'#mydiv', data:{ name:'lele', age:23, job:'coder' }, timmer(){ return setInterval(()=>console.log(666),1000) }, methods:{ myclick(){ console.log('myclick'); this.name = '乐乐'; this.age = 99; } }, destoryed(){ console.log('destoryed') console.log(this)//无法访问this,只剩下dom空壳 } }) |
errorCaptured :当捕获一个来自子孙组件的错误时被调用。此钩子会收到三个参数:错误的对象、发生错误的组件实例、错误来源信息的字符串。此钩子可以返回 false
以阻止该错误继续向上传播。
你可以在此钩子中修改组件的状态。因此在捕获错误时,在模板或渲染函数中有一个条件判断来绕过其它内容就很重要;不然该组件可能会进入一个无限的渲染循环。
错误传播规则
config.errorHandler
被定义,所有的错误仍会发送它,因此这些错误仍然会向单一的分析服务的地方进行汇报。errorCaptured
钩子,则它们将会被相同的错误逐个唤起。errorCaptured
钩子自身抛出了一个错误,则这个新错误和原本被捕获的错误都会发送给全局的 config.errorHandler
。errorCaptured
钩子能够返回 false
以阻止错误继续向上传播。本质上是说“这个错误已经被搞定了且应该被忽略”。它会阻止其它任何会被这个错误唤起的 errorCaptured
钩子和全局的 config.errorHandler
。