前言
生命周期通俗点说是一个对象的生老病死(Cradle-to-Grave)但对于Vue而言,Vue的生命周期也就是 Vue实例及组件实例从创建到卸载的过程。
Vue的生命周期
Vue 实例有一个完整的生命周期,也就是从开始创建、初始化数据、编译模版、挂载Dom -> 渲染、更新 -> 渲染、卸载等一系列过程,我们称这是Vue的生命周期。通俗说就是Vue实例从创建到销毁的过程。同时在这个过程中也会运行一些叫做生命周期钩子的函数,这给了用户在不同阶段添加自己的代码的机会。
以下根据vue官网生命周期图示展开讲解
相关代码及实例均可clone: https://github.com/yanglikun1/bascVue.git 下载。
Vue生命周期钩子函数
beforeCreate
beforeCreate:在实例初始化之后,数据观测和event/watcher事件配置之前调用。
如何理解此钩子,堆上代码最直接:
[代码1-1]
beforeCreate() {
window.console.log(this);
window.console.log(`el: ${this.$el}`);
window.console.log(`data : ${this.$data}`);
},
this 表示实例本身在此钩子中实例已经创建
this.$el 表示实例挂载的dom,输出 undefined 说明 实例尚未挂载
this.$data 表示 Vue 实例观察的数据对象 输出undefined 说明此时数据并未被实例观测到。
根据执行结果提示 beforeCreate 的解释是“创建实例之前执行的钩子事件”:对此处的理解是 数据观测、event/watch的配置是实例的一部分,beforeCreate只是new 了实例。
created
created:在实例创建完成后被立即调用。在这一步,实例已完成数据观测、属性和方法的运算,watch/event事件回调。然而,挂载阶段还没开始,$el属性目前不可见。
[代码2-1]
created() {
window.console.log(`el : ${this.$el}`);
window.console.log(`data : ${JSON.stringify(this.$data)}`);
window.console.log(`msg: ${this.msg}`);
},
Vue会自动将data里面的数据进行递归抓换成getter和setter进行观测;
this.$el 输出 undefined 说明 实例尚未挂载
this.$data 输出数据对象说明此时数据实例观测到data。
this.msg 数据data已经实现geter/seter转换。属性和方法的运算已配置。
对比 beforeCreate下this实例和created 中this实例的区别。
el选项的有无对生命周期过程的影响
首先系统会判断对象中有没有el选项,有el选项,则继续编译过程;没有el选项,则停止编译,也意味着暂时停止了生命周期,直到 vm.$mount(el),页面此时必需已有el存在否则报错。
- 注释
new Vue() 中el选项查看浏览器consle;
[代码3-1]
new Vue({
// el: '#first',
data() {
return {
msg: 30,
};
},
// template: '<App/>',
beforeCreate() {
window.console.log('调用了beforCreate函数===============');
},
created() {
window.console.log('调用了created函数===================');
},
beforeMount() {
window.console.log('调用了beforeMount函数=============');
},
mounted() {
window.console.log('调用了mounted函数===================');
},
正如说明中所说停止编译,也意味着暂时停止了生命周期。
2.在created钩子中 添加 this.$mount('#first') 查看浏览器consle;
[代码3-2]
new Vue({
// el: '#first',
data() {
return {
msg: 30,
};
},
// template: '<App/>',
beforeCreate() {
window.console.log('调用了beforCreate函数===============');
},
created() {
window.console.log('调用了created函数===================');
this.$mount('#first');
},
beforeMount() {
window.console.log('调用了beforeMount函数=============');
},
mounted() {
window.console.log('调用了mounted函数===================');
},
使用this.$mount 指定挂载dom 生命周期继续。
3.将this.$mount('#app') 放在最前和最后查看区别。
[代码3-3]
new Vue({
// el: '#first',
data() {
return {
msg: 30,
};
},
// template: '<App/>',
beforeCreate() {
window.console.log('调用了beforCreate函数===============');
},
created() {
this.$mount('#first');
window.console.log('调用了created函数===================');
},
beforeMount() {
window.console.log('调用了beforeMount函数=============');
},
mounted() {
window.console.log('调用了mounted函数===================');
},
如果在beforeCreate中使用this.$mount() ,会出现什么情况?
template参数选项的有无对生命周期的影响
- 如果Vue实例对象中有template参数选项,则将其作为模版编译浏览器可读的HTML;
- 如果没有template参数选项,则外部的html作为模版编译,也就是说template参数的优先级要比html高。
修改外部html中挂载元素id为first,修改new Vue()中el选项值为#first 并注释掉template参数,在beforeMount/mounted中打印this.$el查看。
[代码4-1]
new Vue({
el: '#first',
data() {
return {
msg: 30,
age: 1,
number: 0,
};
},
router,
// template: '<App/>',
beforeCreate() {
window.console.log('调用了beforCreate函数===============');
},
created() {
window.console.log('调用了created函数===================');
},
beforeMount() {
window.console.log('调用了beforeMount函数=============');
window.console.log(this.$el);
},
mounted() {
window.console.log('调用了mounted函数===================');
window.console.log(this.$el);
},
我们可以看到在beforMount中数据尚未渲染到dom;
为什么说template比外部模版优先级高呢?咱们继续往下
打开注释掉template参数,在beforeMount/mounted中打印this.$el查看。
[代码4-2]
new Vue({
el: '#first',
data() {
return {
msg: 30,
age: 1,
number: 0,
};
},
router,
template: '<App/>',
beforeCreate() {
window.console.log('调用了beforCreate函数===============');
},
created() {
window.console.log('调用了created函数===================');
},
beforeMount() {
window.console.log('调用了beforeMount函数=============');
window.console.log(this.$el);
},
mounted() {
window.console.log('调用了mounted函数===================');
window.console.log(this.$el);
},
可以发现mounted 中的this.$el 被替换成了app, new Vue() 实例的template的优先级高于外部html 。
beforeMount
beforeMount:在挂载开始之前被调用,相关的render函数首次被调用。
上例中可以看到 在此时模版已经被编译但尚未渲染;
思考:此时template模版中的组件的状态
[代码5-1]
beforeCreate() {
window.console.log('App.vue调用了beforCreate函数');
},
created() {
window.console.log('App.vue调用了created函数');
},
beforeMount() {
window.console.log('App.vue调用了beforeMount函数');
},
mounted() {
window.console.log('App.vue调用了mount函数');
},
beforeUpdate() {
window.console.log('App.vue调用了beforeUpdate函数');
},
updated() {
window.console.log('App.vue调用了updated函数');
},
此时template模版中的组件实例已经挂载到相应的dom上,并且在Vue 实例完成挂载之前。
mounted
mounted:el 被新创建的vm.$el替换,并挂载到实例上去之后调用该钩子。如果root实例挂载了一个文档内元素,当 mounted 被调用时vm.$el也在文档内。
注意 mounted 不会承诺所有的子组件也都一起被挂载。如果你希望等到整个视图都渲染完毕,可以用 vm.$nextTick 替换掉 mounted。
beforeUpdate
beforeUpdate:数据更新时调用,发生在虚拟dom重新渲染和打补丁之前。
(只有Vue实例中的数据被“写入”到我们的模板中)你可以在这个钩子中进一步的更改状态,这不会触发附加的重渲染过程。
[代码6-1]
<body>
<div id="first" style="font-size:50px;">{{msg}} {{age}}
<button @click="msg = 2">修改msg</button>
<button @click="number = 2">修改number</button>
</div>
<!-- built files will be auto injected -->
</body>
new Vue({
el: '#first',
data() {
return {
msg: 30,
age: 1,
number: 0,
};
},
beforeUpdate() {
window.console.log('调用了beforeUpdate函数=============');
},
updated() {
window.console.log('调用了updated函数==================');
}
点击"修改number"按钮结果是:没有触发beforUpdate/updated。
updated
updated:由于数据更改导致的虚拟DOM重新渲染和打补丁,在这之后会调用该钩子。当这个钩子被调用时,组件DOM已经更新,所以你现在可以执行依赖于 DOM 的操作。
然而在大多数情况下,你应该避免在此期间更改状态。如果要相应状态改变,通常最好使用计算属性或 watcher 取而代之。注意 updated 不会承诺所有的子组件也都一起被重绘。如果你希望等到整个视图都重绘完毕,可以用 vm.$nextTick 替换掉 updated;
测试发现数据的改变引起的只是本实例的beforeUpdate/updated的触发。
activated
activated:keep-alive 组件激活时调用,组件第一次执行时激活keep-alive。
[代码7-1]
<div id="app">
<div class="demo">
<keep-alive>
<component :is="view"></component>
</keep-alive>
<button @click="view = 'HelloWorld'">点击切换</button>
</div>
template 选项的模版组件,初始化触发了 TestKeep子组件的activate 钩子函数。
deactivated
deactivated:keep-alive 组件停用时调用。
当组件在 <keep-alive> 内被切换,它的 activated 和 deactivated 这两个生命周期钩子函数将会被对应执行。
按照[代码7-1] 点击 “点击切换” 按钮
我们看到子组件 TeatKeep 调用了deactivated,HelloWorld组件的activate触发。
beforeDestroy
beforeDestroy:实例销毁之前调用。在这一步,实例仍然完全可用。
[代码8-1]
<div id="app">
<div class="demo">
<p><b>beforeDestroy</b>:实例销毁之前调用。在这一步,实例仍然完全可用。</p> <button @click="destoryd" >销毁App</button>
<p><b>age = {{age}}</b></p>
</div>
</div>
<!-- destoryd() 函数 执行 this.$destory-->
beforeCreate() {
window.console.log('App.vue调用了beforCreate函数');
},
created() {
window.console.log('App.vue调用了create函数');
},
beforeMount() {
window.console.log('App.vue调用了beforeMount函数');
},
mounted() {
window.console.log('App.vue调用了mount函数');
},
beforeUpdate() {
window.console.log('App.vue调用了beforeUpdate函数');
},
updated() {
window.console.log('App.vue调用了Update函数');
},
beforeDestroy() {
window.console.log('App.vue调用了beforeDestroy函数');
this.age = 50;
window.console.log(this.age);
// 此钩子里面修改age 数据。
},
destroyed() {
window.console.log('App.vue调用了destroyed函数');
},
此时可以获取实例,仍触发updated函数,但不会触发beforUpdate函数,页面不再被渲染。
destroyed
destroyed:Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。
[代码9-1]
<div id="app">
<!-- <div class="fileimge"></div> -->
<div class="demo">
<p><b>beforeDestroy</b>:实例销毁之前调用。在这一步,实例仍然完全可用。</p> <button @click="destoryd" >销毁App</button>
<p><b>destroyed</b>:Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。</p>
<p><b>age = {{age}}</b></p>
<component :is="view"></component>
<!--TestKeep子组件-->
</div>
</div>
点击‘销毁App’ 的结果
此时可以获取实例,但不能触发updated函数,子实例被销毁,修改v-show不会触发渲染。仍可获取this 但是关于实例的监听及子实例均已销毁,触发了子组件的销毁钩子,在点击'销毁App'按钮 已经没有效果。
errorCaptured
errorCaptured:当捕获一个来自子孙组件的错误时被调用。此钩子会收到三个参数:错误对象、发生错误的组件实例以及一个包含错误来源信息的字符串。此钩子可以返回 false 以阻止该错误继续向上传播。
本身报错此钩子不会触发。
[代码10-1]
// 子组件抛出 错误
errorCaptured(err, vm, info) {
window.console.log('error================');
const error = `${err.stack}\n\nfound in ${info} of component`;
window.console.log(error);
return false; // 阻止向上传播;
}
总结
讲完这些Vue生命周期钩子我们还会有很多疑惑,不过通过我们的不断探索会发现更多的使用场景和方式。你我共勉!!!
前言
生命周期通俗点说是一个对象的生老病死(Cradle-to-Grave)但对于Vue而言,Vue的生命周期也就是 Vue实例及组件实例从创建到卸载的过程。
Vue的生命周期
Vue 实例有一个完整的生命周期,也就是从开始创建、初始化数据、编译模版、挂载Dom -> 渲染、更新 -> 渲染、卸载等一系列过程,我们称这是Vue的生命周期。通俗说就是Vue实例从创建到销毁的过程。同时在这个过程中也会运行一些叫做生命周期钩子的函数,这给了用户在不同阶段添加自己的代码的机会。
Vue生命周期钩子函数
beforeCreate
beforeCreate:在实例初始化之后,数据观测和event/watcher事件配置之前调用。
如何理解此钩子,堆上代码最直接:
[代码1-1]
this表示实例本身在此钩子中实例已经创建this.$el表示实例挂载的dom,输出 undefined 说明 实例尚未挂载this.$data表示 Vue 实例观察的数据对象 输出undefined 说明此时数据并未被实例观测到。created
created:在实例创建完成后被立即调用。在这一步,实例已完成数据观测、属性和方法的运算,watch/event事件回调。然而,挂载阶段还没开始,$el属性目前不可见。
[代码2-1]
this.$el输出 undefined 说明 实例尚未挂载this.$data输出数据对象说明此时数据实例观测到data。this.msg数据data已经实现geter/seter转换。属性和方法的运算已配置。对比 beforeCreate下this实例和created 中this实例的区别。
el选项的有无对生命周期过程的影响
首先系统会判断对象中有没有el选项,有el选项,则继续编译过程;没有el选项,则停止编译,也意味着暂时停止了生命周期,直到
vm.$mount(el),页面此时必需已有el存在否则报错。new Vue()中el选项查看浏览器consle;[代码3-1]
正如说明中所说停止编译,也意味着暂时停止了生命周期。
2.在created钩子中 添加
this.$mount('#first')查看浏览器consle;[代码3-2]
使用
this.$mount指定挂载dom 生命周期继续。3.将
this.$mount('#app')放在最前和最后查看区别。[代码3-3]
template参数选项的有无对生命周期的影响
修改外部html中挂载元素id为first,修改
new Vue()中el选项值为#first 并注释掉template参数,在beforeMount/mounted中打印this.$el查看。[代码4-1]
为什么说template比外部模版优先级高呢?咱们继续往下
打开注释掉template参数,在beforeMount/mounted中打印
this.$el查看。[代码4-2]
beforeMount
beforeMount:在挂载开始之前被调用,相关的render函数首次被调用。
上例中可以看到 在此时模版已经被编译但尚未渲染;
思考:此时template模版中的组件的状态
[代码5-1]
mounted
mounted:el 被新创建的
vm.$el替换,并挂载到实例上去之后调用该钩子。如果root实例挂载了一个文档内元素,当 mounted 被调用时vm.$el也在文档内。beforeUpdate
beforeUpdate:数据更新时调用,发生在虚拟dom重新渲染和打补丁之前。
[代码6-1]
点击"修改number"按钮结果是:没有触发beforUpdate/updated。
updated
updated:由于数据更改导致的虚拟DOM重新渲染和打补丁,在这之后会调用该钩子。当这个钩子被调用时,组件DOM已经更新,所以你现在可以执行依赖于 DOM 的操作。
测试发现数据的改变引起的只是本实例的beforeUpdate/updated的触发。
activated
activated:keep-alive 组件激活时调用,组件第一次执行时激活keep-alive。
[代码7-1]
template 选项的模版组件,初始化触发了 TestKeep子组件的activate 钩子函数。
deactivated
deactivated:keep-alive 组件停用时调用。
当组件在
<keep-alive>内被切换,它的 activated 和 deactivated 这两个生命周期钩子函数将会被对应执行。按照[代码7-1] 点击 “点击切换” 按钮
我们看到子组件 TeatKeep 调用了deactivated,HelloWorld组件的activate触发。
beforeDestroy
beforeDestroy:实例销毁之前调用。在这一步,实例仍然完全可用。
[代码8-1]
此时可以获取实例,仍触发updated函数,但不会触发beforUpdate函数,页面不再被渲染。
destroyed
destroyed:Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。
[代码9-1]
点击‘销毁App’ 的结果
此时可以获取实例,但不能触发updated函数,子实例被销毁,修改v-show不会触发渲染。仍可获取this 但是关于实例的监听及子实例均已销毁,触发了子组件的销毁钩子,在点击'销毁App'按钮 已经没有效果。
errorCaptured
errorCaptured:当捕获一个来自子孙组件的错误时被调用。此钩子会收到三个参数:错误对象、发生错误的组件实例以及一个包含错误来源信息的字符串。此钩子可以返回 false 以阻止该错误继续向上传播。
本身报错此钩子不会触发。
[代码10-1]
总结
讲完这些Vue生命周期钩子我们还会有很多疑惑,不过通过我们的不断探索会发现更多的使用场景和方式。你我共勉!!!