前言:
你是不是已经读了很多篇关于Vue的生命周期的文章了,读完给你的感觉是不是似懂非懂?如果是的话,那么恭喜你,因为你是一个正常人。对于一个Vue小白或者前端小白来说,什么生命周期啊,钩子啊这些个概念是不可能看几篇文章就能彻底弄懂的,包括虚拟dom,双向绑定这些个Vue的核心原理也一样,需要建立在强大的JavaScript的底层基础和Vue项目的实战经验上才可以彻彻底底地搞清楚,理解透,因为只有你的JavaScript基础牢固,阅读Vue源码或者是文章中的示例代码才能轻松理解,只有你做过Vue的实战项目才能体会到Vue这个框架是如何解决实际问题的。所以,不要心急,一步一步地来。这篇文章我会尽最大努力给你们讲一讲Vue的生命周期这个东西到底是什么,就算你们不能100%理解,但我保证,读了这篇文章一定会让你有所收获。
正文:
一.生命周期的概念
【下图截取于百度百科】
生命周期不是vue独有的,也不是我们搞IT的独有的,它是自然界的一个通用的概念,个人觉得vue的生命周期只不过是vue的作者在开发vue的时候为了描述vue的实例从被创建到被销毁经历的这样一个过程,这个实例就好像是我们人一样,从出生到死亡要经历一个过程,对于人来说这样的一个过程就是一个人的生命周期,对于JavaScript对象来说这样的一个过程就是一个对象的生命周期。
(补充:在这里的实例指的是通过new Vue()构造出来的一个JavaScript对象,你要是不爱叫他实例叫他对象也行,在这里指的都一样,下文再说到实例指的都是这个对象,不再做阐述)。
下面我们通过将人的生命周期和vue的实例的生命周期进行一个对比,让你更加深刻的理解这样一个从被创建到被销毁经历的这样一个过程。
首先我们先实例化一个Vue的对象(就是为了取个名字下面做比较用),就叫 vm 吧
var vm = new Vue()
好了,接下来我们就拿“小明”做比较吧
两个对象都准备好了,vm 和 小明
vm | 小明 |
---|---|
beforeCreate | 小明在妈妈肚子里孕育着,此时的小明只是一个肉团,他的父母还没有给他取名叫小明,他无名无姓,什么也做不了 |
created | 小明出生了,有了自己的名字并且会哭会爬,开始一天天的长大 |
beforeMount | 小明大学毕业了(完全掌握了人际交往的法则和工作所需的技能,和我们毕业的时候一样,四处投简历,开始找工作) |
mounted | 小明上班了(找到了自己心仪的工作单位,正式融入社会,有了自己的社交圈,每个月拿着5k的工资,网购的时候快递小哥可以根据他的单位名找到他了) |
beforeUpdate | 小明每天拼命工作,自身价值提升巨大,和老板谈加薪,老板考虑中 |
updated | 老板同意加薪,薪水增加,由5k立刻涨到了10k |
beforeDestroy | 小明老了,被查出患有癌症晚期,但是可以存活6个月,6个月内小明这个名字仍然在国家在世人口的清单上,且小明仍然具备哭和爬的能力 |
destroyed | 6个月到了,小明死了,国家开出死亡证明,小明正式从在世人口清单上被移除,从此世界在世人口上没有了小明这个人,叫小明不会有人回答,他也不再能够哭和爬了 |
鉴于我不想糊弄又想将vue的生命周期以最通俗,最形象,最易懂,最细节的前提下通过故事描述出来,我真的死了好多脑细胞,哎,头发又掉二两啊。。。我希望你可以将这个表格仔细地看一遍,先看一遍就可以了,你现在不需要记住什么,头脑里有个生命周期印象,有个这样一个过程的印象就好了,因为重点在下面,下面我会通过代码的方式讲述每一个钩子函数的作用:
如果你想最大程度地掌握Vue的生命周期,我希望你不要复制代码,希望你可以和我一起把下面的代码一字一字的敲出来:
<!DOCTYPE html>
<!--
@title:vue的生命周期
@作者:zoulixun
@时间:2019-10-12
-->
<html>
<head>
<meta charset="utf-8">
<title>vue的生命周期</title>
<link href="https://cdn.bootcss.com/twitter-bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
</head>
<body>
<div id="app">
<h1 id="message" style="margin-left: 15px;">{{msg}}</h1>
<button class="btn btn-primary" type="button" @click="updateMsg">更新msg</button>
<button class="btn btn-danger" type="button" @click="destroyVM">销毁实例</button>
</div>
<script type="text/javascript">
var vm = new Vue({ //开始创建一个实例对象
el:"#app", //Vue实例的挂载目标
data:{ //vm对象的属性
msg:"我爱中国"
},
methods:{ //vm对象的方法
print(){
return "print方法";
},
updateMsg(){
this.msg = "I love China";
},
destroyVM(){
this.$destroy();
}
},
/* 以下开始为整个vm实例的生命周期 */
beforeCreate(){
console.warn("---------beforeCreate-------------------这是第一个生命周期函数,实例还没有被完全创建出来,data和methods都不存在,所以打印data是undefined,调用print方法提示找不到,因为此时实例都还有被创建,模板还未编译,所以el也是undefined,因为调用print方法报错导致js语句停止执行,所以38行的获取dom的语句不会执行,所以控制台看不到38行的打印内容");
console.log("el:" + this.$el);
console.log("data:" + this.msg);
console.log("methods:" + this.print());
console.log("获取dom:" + document.getElementById('message').innerHTML);
},
created(){
console.warn("---------created------------------这是第二个生命周期函数,实例被创建出来,这个时候data和methods都已经被初始化好了,所以data可以被正常地打印出来,print方法可以被正常的调用,这个时候Vue开始在内存中编译模板,处于编译中(ing)并未编译好的一个状态,所以此时el也是undefined,所以获取dom打印出来的只是{{msg}}");
console.log("el:" + this.$el);
console.log("data:" + this.msg);
console.log("methods:" + this.print());
console.log("获取dom:" + document.getElementById('message').innerHTML);
},
beforeMount(){
console.warn("---------beforeMount------------------这是第三个生命周期函数,data和methods正常打印就不多说了,在上面的created生命周期中我们不是说模板处于正在编译的一个状态吗,那么此时模板已经在内存中编译完成了,所以打印el会输出[Object HTMLDivElemet]这样一个元素,但是此时模板只是在内存中编译完成还没有挂载到页面中,所以获取dom打印出来的还是{{msg}}");
console.log("el:" + this.$el);
console.log("data:" + this.msg);
console.log("methods:" + this.print());
console.log("获取dom:" + document.getElementById('message').innerHTML);
},
mounted(){
console.warn("---------mounted------------------这是第四个生命周期函数,此时内存中的已经编译好的模板已经真实的挂载到了页面中,一个渲染好的页面呈现在了我们眼前,此时的打印结果想必大家已经知道原因了。");
console.log("el:" + this.$el);
console.log("data:" + this.msg);
console.log("methods:" + this.print());
console.log("获取dom:" + document.getElementById('message').innerHTML);
console.group("说明:mounted是实例创建过程中的最后一个生命周期函数,当mounted执行完毕就表示实例已经被完全创建好了,如果没有其它操作,这个实例会一直保存在内存中,此时创建阶段正式结束,进入运行阶段,")
console.groupEnd();
},
beforeUpdate(){
console.warn("---------beforeUpdate------------------这是第五个生命周期函数,也是组件运行阶段的第一个生命周期函数,点击更新按钮时会触发该方法,这一步先根据data中最新的数据,在内存中重新编译好一个最新的模板,这个最新的模板在该生命周期中会被编译好,但不会挂载到真实的页面中,所以data中打印出来的是更新后的最新的数据,而获取dom打印出来的还是旧的数据");
console.log("el:" + this.$el);
console.log("data:" + this.msg);
console.log("methods:" + this.print());
console.log("获取dom:" + document.getElementById('message').innerHTML);
},
updated(){
console.warn("---------updated------------------这是第六个生命周期函数,也是组件运行阶段的第二个生命周期函数,此时内存中已经编译好的最新的模板会挂载到真正的页面中,这个时候就完成了data(model)层到view(视图)层的更新,页面同步刷新,所以获取dom的时候会打印出最新的数据“I love China”");
console.log("el:" + this.$el);
console.log("data:" + this.msg);
console.log("methods:" + this.print());
console.log("获取dom:" + document.getElementById('message').innerHTML);
},
beforeDestroy(){
console.warn("---------beforeDestroy------------------这是第七个生命周期函数,当执行该钩子函数的时候,Vue实例从---运行阶段---进入到了---销毁阶段---但是此时实例上的data、methods以及过滤器和指令等都处于可用状态,该实例还未真正销毁。");
console.log("el:" + this.$el);
console.log("data:" + this.msg);
console.log("methods:" + this.print());
console.log("获取dom:" + document.getElementById('message').innerHTML);
},
destroyed(){
console.warn("---------destroyed------------------这是第七个生命周期函数,当执行该钩子函数的时候,实例完全销毁,魂飞魄散,自然而然地,实例上的所有数据,方法,指令等等都不再可用。");
console.log("el:" + this.$el);
console.log("data:" + this.msg);
console.log("methods:" + this.print());
console.log("获取dom:" + document.getElementById('message').innerHTML);
}
})
</script>
</body>
</html>
1.浏览器运行此html文件会得到下图内容:
2.清除控制台内容,点击“更新msg”按钮,会得到下图内容:
3.清除控制台内容,点击“销毁实例”按钮,会得到下图内容:
写到这里,本文就接近尾声了,因为其他想说的都在上面的代码中了,希望大家结合控制台中输出的代码执行结果和那些对应的“黄色警告“(这些“警告“中的说明是本文的 “重中之重 ”)能够对Vue生命周期的每一个钩子函数有一个深刻的理解,再回过头对应着看一遍小明的故事,我想聪明的你一定可以我得到收获,笔芯 ~
【本文纯手打,如有纰漏,还请指出,原创文章,转载请注明出处,谢谢】