1.VUE的生命周期的简介
对于使用vue.js来开发,那我们就必须知晓vue的生命周期,才能愉快的玩耍,而且也是面试的高频,但是vue官方给出的文档,那叫一个简单啊,如下图:
但是,如果只是看上面的生命周期图,对于新手来说, 有点云里雾里,接下来,就是我对VUE的生命周期的理解.
2.VUE的生命周期的详情
Vue实例有一个完整的生命周期,也就是说从开始创建、初始化数据、编译模板、挂在DOM、渲染-更新-渲染、卸载等一系列过程,我们称为Vue实例的生命周期. 钩子函数,就是上图中,红色椭圆框那些,比如: beforeCreate, created,beforeMount,mounted,beforeUpdate,updated,beforeDestroy,destroyed这些.
钩子函数类似一个监听器,当Vue实例位于生命某个阶段时, 那对应的钩子函数就会被调用.那我们就可以编写对应的钩子函数, 让Vue在某个阶段给我们一个做某些处理的机会.我们理解Vue的生命周期,重点就是理解钩子函数的执行情况.
2.1 beforeCreate
beforeCreate函数是在实例初始化(new Vue())之后,数据观测(data observer) 和 event/watcher 事件配置之前被调用。也就是说, beforeCreate函数执行是在new Vue()这行代码执行,但是对Vue实例的属性还没有进行初始化的时候调用的,所有我们无法获取到在Vue实例中data定义的数据与在methods声明的函数,以及watcher中的事件都不能获得到. 废话不多说, 我们之间代码演示:
<div id="app" >
<input type="text" v-model="name" />
欢迎:{{name}}
</div>
<script src="./node_modules/vue/dist/vue.js" type="text/javascript"></script>
<script type="text/javascript">
const app = new Vue({
el:'#app',
data:{
name:'zhangsan',
},
methods:{
fun1(){
console.log('fun1....');
}
},
watch:{
name(newVal, oldVal){
console.log(newVal+"-->"+oldVal);
}
},
beforeCreate(){
console.log("组件实例化之前");
console.log(this.name);
console.log(this.$el);
this.fun1();
}
});
</script>
结果如下:
可以看到Vue中的data和方法都是去不到的
2.2 created
实例已经创建完成之后被调用。在这一步,实例已完成以下的配置:数据观测(data observer),属性和方法的运算, watch/event 事件回调。然而,挂载阶段还没开始, e l 属 性 目 前 不 可 见 。 也 就 是 V u e 实 例 的 初 始 化 完 成 , 在 这 个 钩 子 函 数 中 , 我 们 可 以 调 用 d a t a 中 的 数 据 , 以 及 m e t h o d s 的 函 数 , 但 是 我 们 还 无 法 获 取 el 属性目前不可见。也就是Vue实例的初始化完成,在这个钩子函数中,我们可以调用data中的数据,以及methods的函数,但是我们还无法获取 el属性目前不可见。也就是Vue实例的初始化完成,在这个钩子函数中,我们可以调用data中的数据,以及methods的函数,但是我们还无法获取el对象,而且这个时候, 页面上的使用vue指令生成的标签也无法得到.
<div id="app" >
<input type="text" v-model="name" />
欢迎:{{name}}
<ul>
<li v-for="(item,index) of list" :key="index">{{item}}</li>
</ul>
<p v-text="num">p1</p>
<p>p1</p>
<p>p1</p>
</div>
<script type="text/javascript">
const app = new Vue({
el:'#app',
data:{
name:'zhangsan',
list:[],
num:0,
},
methods:{
fun1(){
console.log('fun1....');
}
},
watch:{
name(newVal, oldVal){
console.log(newVal+"-->"+oldVal);
}
},
created(){
console.log("组件实例化完成");
console.log(this.name);
console.log(this.$el);
this.fun1();
//因为我们是通过v-for循环遍历li,所以created之前挂载阶段还没开始.是无法获取li的个数的
console.log('li数量:',document.getElementsByTagName('li').length);
//直接加载出来的DOM是可以直接获取到的
console.log('p个数:',document.getElementsByTagName('p').length);
},
});
</script>
结果:
可以看到:created钩子可以获取Vue的data,调用Vue方法,获取原本HTML上的直接加载出来的DOM,但是无法获取到通过挂载模板生成的DOM(例如:v-for循环遍历Vue.list生成li)
我们一般在create钩子函数中进行调用数据,调用方法,调用异步函数得加载数据的操作
2.3 beforeMount
在挂载开始之前被调用:相关的 render 函数首次被调用,这个时候,我们的$el已经创建,但是通过vue指令生成的标签还是无法获取到,因为我们还没有挂载到页面上.
beforeMount(){
console.log("组件挂载之前");
console.log(this.name);
console.log(this.$el);
this.fun1();
//因为我们是通过v-for循环遍历li,所以created之前挂载阶段还没开始.是无法获取li的个数的
console.log('li数量:',document.getElementsByTagName('li').length);//获取的是页面初始标签, 通过vue生成的变化还没有挂载
//直接加载出来的DOM是可以直接获取到的
console.log('p个数:',document.getElementsByTagName('p').length);
}
结果:
通过结果,我们发现
e
l
现
在
有
值
了
,
说
明
el现在有值了,说明
el现在有值了,说明el已经创建好了,但是li的数量还是1,不是3个,这是因为Vue生成的标签还没有挂载到页面
2.4 mounted
el 被新创建的 vm.$el 替换,并挂载到实例上去之后调用该钩子。
有初始值的DOM渲染,例如我们的初始数据list,渲染出来的li,只有这里才能获取.
mounted(){
console.log("组件挂载完成");
console.log(this.name);
console.log(this.$el);
this.fun1();
console.log('li数量:',document.getElementsByTagName('li').length);
console.log('p个数:',document.getElementsByTagName('p').length);
}
结果:
我们看到现在的li数量是3了,可以看到到这里为止,挂载到实例上了, 这时dom节点被渲染到文档内,一些需要dom的操作在此时才能正常进行,所以我们可以获取到li的个数了.
2.5 beforeUpdate
数据更新时调用,发生在虚拟 DOM 重新渲染和打补丁之前。这里需要注意的是beforeUpdate是在data数据被改变后触发,这里的data数据改变如何理解,仅从字面上理解只要data数据值改变就会触发beforeUpdate吗?答案是否定的,我们做个测试。
<div id="app" >
<input type="text" v-model="name" />
欢迎:{{name}}
</div>
<script type="text/javascript">
const app = new Vue({
el:'#app',
data:{
name:'zhangsan',
num : 0,
},
mounted(){
console.log('组件加载完成');
console.log('修改之前:num='+this.num);
this.num++;
console.log('修改之后:num='+this.num);
},
beforeUpdate(){
console.log('组件更新之前');
console.log('num='+this.num);
}
});
结果:
通过上面结果,我们发现beforeUpdate()函数并没有执行,这是为什么呢?
原因在于beforedate是针对视图层,视图层的数据发生改变才会触发, 而我们的num没有在页面标签使用,虽然num值改变了,但是,它也不会触发beforeUpdate()函数.那如果我们在标签使用这个num,那会不会触发beforeUpdate()函数呢?我们来做一个测试:
我们在页面添加如下代码:而js代码不做任何改变,我们再看一下结果会是怎么样?
<h1>{{num}}</h1>
结果:
此时,我们发现beforeUpdate()函数触发了,为什么这个时候,beforeUpdate()函数会触发呢? 这是因为我们在页面使用了这个num,也就是这个num属于视图层的数据,所以修改num,就会触发beforeUpdate()函数. 那如果我们把修改num的代码写在created()函数中, beforeUpdate()函数会触发吗? 大家思考一下.
答案是得分情况, 如果是直接修改的写法,肯定不会触发beforeUpdate()函数, 但是如果是异步加载数据,那这个时候,会触发beforeUpdate()函数,我们来测试一下:
const app = new Vue({
el:'#app',
data:{
name:'zhangsan',
num : 0,
},
methods:{
fun1(){
console.log('fun1....');
}
},
created(){
console.log('组件创建完成完成');
console.log('修改之前:num='+this.num);
setTimeout(()=>{ //使用setTimeout()来模拟异步修改
this.num++;
console.log('修改之后:num='+this.num);
},0);
},
beforeUpdate(){
console.log('组件更新之前');
console.log('num='+this.num);
}
});
结果:
为什么这个beforeUpdate()函数又被触发了呢?
因为是在created的钩子中加入异步函数,所以函数的执行顺序为:
ceated钩子,mounted钩子,异步函数,beforeUpdate钩子,updated钩子,也就是异步函数是在mounted钩子之后执行的, 当mounted钩子执行后,这个num也就在页面使用了,然后在异步函数修改,就会触发.
2.7 updated
由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子。当这个钩子被调用时,组件 DOM 已经更新,所以你现在可以执行依赖于 DOM 的操作。然而在大多数情况下,你应该避免在此期间更改状态,因为这可能会导致更新无限循环。
我们在updated钩子中修改数据,也会触发beforeUpdate钩子.
const app = new Vue({
el:'#app',
data:{
name:'zhangsan',
num : 0,
},
mounted(){
console.log('组件挂载完成');
console.log('修改前:num='+this.num);
this.num=1;
console.log('修改后:num='+this.num);
},
beforeUpdate(){
console.log('组件更新之前');
// console.log('修改前:num='+this.num);
// this.num=2;
// console.log('修改后:num='+this.num);
},
updated(){
console.log('组件更新完成');
console.log('修改前:num='+this.num);
this.num=3;
console.log('修改后:num='+this.num);
}
});
结果:
通过上面结果,我们发现beforeUpdate钩子调用了两次, 第一次是在挂载完成之后,调用mounted钩子函数, 此时在mounted函数修改了num,就触发了beforeUpdate, 当执行完beforeUpdate之后就执行updated钩子, 而我们在updated钩子函数中又一次修改了num,然后导致的虚拟 DOM 重新渲染和打补丁, 重新渲染页面的数据, 又触发了beforeUpdate钩子,所以第二次的beforeUpdate又被触发.
但是如果我们把上面代码中的beforeUpdate注释的代码放开, 你会发现,死循环了.
这是因为,当执行完updated钩子,而在updated()函数内部又修改了num,导致虚拟 DOM 重新渲染和打补丁, 重新渲染页面的数据, 又触发了beforeUpdate钩子, 而beforeUpdate钩子函数内又把num由3修改为2, 当beforeUpdate钩子执行完,就会执行updated钩子,而updated钩子又把num由2修改为3,导致虚拟 DOM 重新渲染和打补丁, 重新渲染页面的数据,从而又导致beforeUpdate触发…
所以就这样一直死循环下去,上面代码也可以把beforeUpdate钩子修改num的代码区别, 而把update修改num的代码修改为this.num++;此时也会是死循环.
应该避免在updated更改状态,因为这可能会导致更新无限循环
2.8 beforeDestroy
实例销毁之前调用。但是Vue实例仍然完全可用。
2.9 destroyed
Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。