Open yanglikun1 opened 6 years ago
生命周期通俗点说是一个对象的生老病死(Cradle-to-Grave)但对于Vue而言,Vue的生命周期也就是 Vue实例及组件实例从创建到卸载的过程。
Vue 实例有一个完整的生命周期,也就是从开始创建、初始化数据、编译模版、挂载Dom -> 渲染、更新 -> 渲染、卸载等一系列过程,我们称这是Vue的生命周期。通俗说就是Vue实例从创建到销毁的过程。同时在这个过程中也会运行一些叫做生命周期钩子的函数,这给了用户在不同阶段添加自己的代码的机会。
以下根据vue官网生命周期图示展开讲解 相关代码及实例均可clone: https://github.com/yanglikun1/bascVue.git 下载。
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
this.$data
根据执行结果提示 beforeCreate 的解释是“创建实例之前执行的钩子事件”:对此处的理解是 数据观测、event/watch的配置是实例的一部分,beforeCreate只是new 了实例。
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.msg
对比 beforeCreate下this实例和created 中this实例的区别。
el选项的有无对生命周期过程的影响
首先系统会判断对象中有没有el选项,有el选项,则继续编译过程;没有el选项,则停止编译,也意味着暂时停止了生命周期,直到 vm.$mount(el),页面此时必需已有el存在否则报错。
vm.$mount(el)
new Vue()
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]
this.$mount('#first')
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 生命周期继续。
this.$mount
3.将this.$mount('#app') 放在最前和最后查看区别。
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参数选项的有无对生命周期的影响
修改外部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:在挂载开始之前被调用,相关的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:el 被新创建的vm.$el替换,并挂载到实例上去之后调用该钩子。如果root实例挂载了一个文档内元素,当 mounted 被调用时vm.$el也在文档内。
vm.$el
注意 mounted 不会承诺所有的子组件也都一起被挂载。如果你希望等到整个视图都渲染完毕,可以用 vm.$nextTick 替换掉 mounted。
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:由于数据更改导致的虚拟DOM重新渲染和打补丁,在这之后会调用该钩子。当这个钩子被调用时,组件DOM已经更新,所以你现在可以执行依赖于 DOM 的操作。
然而在大多数情况下,你应该避免在此期间更改状态。如果要相应状态改变,通常最好使用计算属性或 watcher 取而代之。注意 updated 不会承诺所有的子组件也都一起被重绘。如果你希望等到整个视图都重绘完毕,可以用 vm.$nextTick 替换掉 updated;
vm.$nextTick
测试发现数据的改变引起的只是本实例的beforeUpdate/updated的触发。
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:keep-alive 组件停用时调用。
当组件在 <keep-alive> 内被切换,它的 activated 和 deactivated 这两个生命周期钩子函数将会被对应执行。
<keep-alive>
按照[代码7-1] 点击 “点击切换” 按钮
我们看到子组件 TeatKeep 调用了deactivated,HelloWorld组件的activate触发。
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: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:当捕获一个来自子孙组件的错误时被调用。此钩子会收到三个参数:错误对象、发生错误的组件实例以及一个包含错误来源信息的字符串。此钩子可以返回 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生命周期钩子我们还会有很多疑惑,不过通过我们的不断探索会发现更多的使用场景和方式。你我共勉!!!