刚接触vue,使用vue并没意识到生命周期的重要性。直到项目深入,在解决父组件异步加载数据传给子组件并渲染时首次渲染异常的问题时,浪费了很多时间,才发现自己对vue生命周期理解的欠缺。因此,这次专门重新学习一下vue的生命周期并总结在这里分享,希望对大家有所帮助。
首先上官方大图(是不是很高大上?然而,有点晦涩,对初学者不是很友好!)
不过不用担心,咱们程序员不就是实践出真知嘛!上代码:
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 <meta http-equiv="X-UA-Compatible" content="ie=edge"> 7 <title>Document</title> 8 </head> 9 <body> 10 <div id='example-3'> 11 </div> 12 13 <template id="tmp"> 14 <div> 15 <p class="myp">Vue Component A</p> 16 <button @click="destroy">Destroy</button> 17 <input type="text" v-model="message"> 18 <p>{{message}}</p> 19 </div> 20 </template> 21 <script src="https://cdn.jsdelivr.net/npm/vue@2.5.22/dist/vue.js"></script> 22 <script> 23 let vm = new Vue({ 24 el: '#example-3', 25 template: '#tmp' , 26 data: { 27 message: 'This is default value' 28 }, 29 beforeCreate() { 30 console.log('beforeCreate ==========================='); 31 console.log(this); 32 console.log(this.$el); 33 console.log(this.$data); 34 console.log(this.message); 35 }, 36 created() { 37 console.log('created ==========================='); 38 console.log(this); 39 console.log(this.$el); 40 console.log(this.$data); 41 this.message = 'Hello World'; 42 console.log(this.message); 43 }, 44 beforeMount() { 45 console.log('beforeMount ==========================='); 46 console.log(this.$el); 47 console.log(this.$data); 48 console.log(this.message); 49 }, 50 mounted() { 51 console.log('mounted ==========================='); 52 console.log(this.$el); 53 console.log(this.$data); 54 console.log(this.message); 55 }, 56 beforeUpdate() { 57 console.log('beforeUpdate ==========================='); 58 console.log(this.$el); 59 console.log(this.$data); 60 console.log(this.message); 61 }, 62 updated() { 63 console.log('updated ==========================='); 64 console.log(this.$el); 65 console.log(this.$data); 66 console.log(this.message); 67 }, 68 beforeDestroy() { 69 console.log('beforeDestroy ==========================='); 70 }, 71 destroyed() { 72 console.log('destroyed ==========================='); 73 }, 74 methods: { 75 destroy() { 76 console.log('user destory component'); 77 } 78 } 79 }) 80 </script> 81 </body> 82 </html>
接着我们可以在每一个生命周期钩子函数里面打断点查看每一个你想要知道的变量的创建,初始化,更新,销毁的过程。下面我们就来详细的分析一下:
1. beforeCreate (我们将断点打在Line 35, 得到如下结果)
- Vue对象已被创建 new Vue()
- 初始化vue对象的属性 $options(beforeCreate, created...., methods.....) (这个对应生命周期图的 init events & lifecycle
- this.$data, this.$props等并没有初始化
2. created
由上图我们可以看出,在created阶段vue还是做了很多事情的:
首先会判断对象是否有el选项。如果有的话就继续向下编译,如果没有el选项,则停止编译,也就意味着停止了生命周期,直到在该vue实例上调用vm.$mount(el).
然后,我们往下看,template参数选项的有无对生命周期的影响。
(1).如果vue实例对象中有template参数选项,则将其作为模板编译成render函数。
(2).如果没有template选项,则将外部HTML作为模板编译。
(3).可以看到template中的模板优先级要高于outer HTML的优先级。
这下就可以想想什么el的判断要在template之前了~是因为vue需要通过el找到对应的outer template。
接着我们将断点打在Line 43, 得到如下结果:
- 初始化vue的data 属性, 进行数据的观测
由此可见,我们可以在created勾子里面给data里面的属性复制,它的更新会直接渲染到页面上。 例如, 在created勾子函数里面将message重新赋值,得到新的渲染
页面。
3. beforeMount (我们将断点打在Line 49, 得到如下结果)
- beforeMount勾子函数执行之前,render函数未定义,beforeMount阶段编译器会 compile template into render function
4. 调用Render方法
这里我们在options属性里面添加 render方法,如下:
1 destroyed() { 2 console.log('destroyed ==========================='); 3 }, 4 render() { 5 console.log('rendder ==========================='); 6 },
然后在mounted方法里面打断点,可以看到:
rendder =========================== 先输出
mounted =========================== 后输出
由此可见,render方法是在mounted函数刚进入时就调用了,在beforeMount函数阶段是没有被调用的,只是将template内容加载到render函数里面了。此时,我们可以看dom结构仍然是用{{message}}做占位符的,而非被赋值后的
This is default value
5. mounted
在mounted之前还是通过{{message}}进行占位的,因为此时挂在到页面上的还是JavaScript中的虚拟DOM形式存在的。在mounted之后可以看到div中的内容发生了变化。
6. beforeUpdate 和 updated
当我们在输入框的值后面追加一个 -, 我们去控制台上看: 当断点打在beforeUpdate方法中 Line 59:
可以看到此时,this.$el 中message的值还是原来的 "This is default value"
但是当断点打在updated方法中Line65:
可以看到此时,this.$el 中message的值已经变为新的 "This is default value-"
7. beforeDestory 和 destoryed
如果想要测试这两个勾子函数,我们可以设计一个子组件的案例,然后通过 v-if去控制组件的销毁,从而测试这两个勾子函数。后者在控制台调用 vm.$destory().
beforeDestory方法内一般用于做善后处理,比如我们如果订阅了某个流,然后就需要在beforeDestory函数里面去取消订阅。
总结:
1. beforeCreate - 创建 new vue()对象,初始化$options里面的methods,生命周期勾子;不初始化 $data, $el, $props
2. created - 初始化$data , $props;因此create函数一般用于赋值data,props
3. beforeMount - 初始化 $el, 并将 template里面的内容装载到render函数里面。可通过 vm.$mount(el)触发
4. mounted -> render - 首先调用render函数,形成虚拟dom,然后装载数据,用真实dom替换虚拟dom。当需要操作dom时,我们必须在beforeMount后面的生命周期勾子函数中操作,否则会得到undefined的错误。因为,之前真实的dom还并未装载到dom树上。
5. beforeUpdate - 当data值被修改时会被调用,此时更新并未装载到dom
6. updated - re-render dom
7. beforeDestory - 该函数一般用于取消事件或取消订阅;可通过 vm.$destory()触发
8. destoryed
beforeCreate & created 是创建vue对象的阶段。beforeMount & mounted 装载阶段,beforeMount做装载前的准备,比如 $el的初始化,render函数的准备;mounted就是装载,调用render函数,形成虚拟dom,替换占位符,渲染真实dom。
参考博客: https://segmentfault.com/a/1190000011381906