浅谈vue的生命周期

Posted js前端技术栈

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了浅谈vue的生命周期相关的知识,希望对你有一定的参考价值。

生命周期

在使用vue的过程中,对vue的生命周期的理解是最基础的开始,也许你不需要全部理解,但对其中最主要的几个阶段却要知之甚深,这是你项目中密切相关的。

周期图

这是一张vue官网上面展示的生命周期图,对每一个过程都做了罗列。

周期说明

所有的生命周期钩子函数会自动绑定this上下文到实例中,因此可以访问数据,对属性和方法进行运算。不能使用箭头函数来定义一个生命周期方法。如(created: () => this.fetch()),箭头函数绑定了父上下文,因此this与vue的实例不同,会显示未定义。

  • 1、beforeCreate

在实例初始化之后,数据观测 (data observer) 和 event/watcher 事件配置之前被调用。

在这个阶段可以异步方式获取data数据,使用this.$nextTick或者setTimeout都行,当全执行完之后会执行里面的代码,获取需要的数据。

如果需要同步拿到数据,需要了解数据存放地点,在beforeCreate前,所有的options都会先存到vm.$options中,在beforeCreate之后才会附到vm上,然后再触发created钩子,所以要想在这个时候拿到数据,那直接this.$options.data()['data']获取。

beforeCreate最好不要改动data里的数据,否则可能会出现无法监听的情况,模板中需要使用data进行渲染时,先给data默认的初始值,created之后再进行更改(如ajax)改成需要的值。

  • 2、created

组件实例创建完成,属性已绑定,但DOM还未生成,挂载阶段还没开始,$el属性还不存在。在这一步主要的工作为:调用数据,调用方法,调用异步函数。

在create函数里面是可以获取到data,调用Vue方法,可以获取到原本html上直接加载出来的DOM,但是无法获取到通过挂载模板生成的DOM。比如v-for遍历的内容。在这一步通常会初始化某些属性值,比如通过异步函数获取数据更新data,然后再渲染成视图。

  • 3、beforeMount

在挂载开始之前被调用:render 函数或者模板首次被调用。

  • 4、mounted

el 被新创建的 vm.$el 替换,并挂载到实例上去之后调用该钩子。这个时候DOM会被渲染完成,初始的数据会被渲染完成,在这里才能获取到具体的DOM元素。

  • 5、beforeUpdate

数据更新时调用,发生在虚拟 DOM 重新渲染之前。你可以在这个钩子中进一步地更改状态,这不会触发附加的重渲染过程。当我们更改Vue的任何数据,都会触发该函数。

  • 6、updated

由于数据更改导致的虚拟 DOM 重新渲染,在这之后会调用该钩子。当这个钩子被调用时,组件 DOM 已经更新,所以你现在可以执行依赖于 DOM 的操作。然而在大多数情况下,你应该避免在此期间更改状态,因为这可能会导致更新无限循环。该钩子在服务器端渲染期间不被调用。

  • 7、beforeDestroy

实例销毁之前调用。在这一步,实例仍然完全可用。

  • 8、destroyed

Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。该钩子在服务器端渲染期间不被调用。

var vm = new Vue({
el: '#vue_det',
data: {
info: "This Test"
},
beforeCreate: function() {
console.group('beforeCreate 创建前状态===============》');
console.log("%c%s", "color:red", "el : " + this.$el); //undefined
console.log("%c%s", "color:red", "data : " + JSON.stringify(this.$data)); //undefined
console.log("%c%s", "color:red", "info: " + this.info)
},
created: function() {
console.group('created 创建完毕状态===============》');
console.log("%c%s", "color:red", "el : " + this.$el); //undefined
console.log("%c%s", "color:red", "data : " + JSON.stringify(this.$data)); //已被初始化
console.log("%c%s", "color:red", "info: " + this.info); //已被初始化
},
beforeMount: function() {
console.group('beforeMount 挂载前状态===============》');
console.log("%c%s", "color:red", "el : " + this.$el); //已被初始化
console.log(this.$el);
console.log("%c%s", "color:red", "data : " + JSON.stringify(this.$data)); //已被初始化
console.log("%c%s", "color:red", "info: " + this.info); //已被初始化
},
mounted: function() {
console.group('mounted 挂载结束状态===============》');
console.log("%c%s", "color:red", "el : " + this.$el); //已被初始化
console.log(this.$el);
console.log("%c%s", "color:red", "data : " + JSON.stringify(this.$data)); //已被初始化
console.log("%c%s", "color:red", "info: " + this.info); //已被初始化
},
beforeUpdate: function() {
console.group('beforeUpdate 更新前状态===============》');
console.log("%c%s", "color:red", "el : " + this.$el);
console.log(this.$el);
console.log("%c%s", "color:red", "data : " + JSON.stringify(this.$data));
console.log("%c%s", "color:red", "info: " + this.info);
},
updated: function() {
console.group('updated 更新完成状态===============》');
console.log("%c%s", "color:red", "el : " + this.$el);
console.log(this.$el);
console.log("%c%s", "color:red", "data : " + JSON.stringify(this.$data));
console.log("%c%s", "color:red", "info: " + this.info);
},
beforeDestroy: function() {
console.group('beforeDestroy 销毁前状态===============》');
console.log("%c%s", "color:red", "el : " + this.$el);
console.log(this.$el);
console.log("%c%s", "color:red", "data : " + JSON.stringify(this.$data));
console.log("%c%s", "color:red", "info: " + this.info);
},
destroyed: function() {
console.group('destroyed 销毁完成状态===============》');
console.log("%c%s", "color:red", "el : " + this.$el);
console.log(this.$el);
console.log("%c%s", "color:red", "data : " + JSON.stringify(this.$data));
console.log("%c%s", "color:red", "info: " + this.info)
}
})

第一次运行,输出结果如下:

当在控制输入vm.info = "rewrite"对info进行更新时:在beforeUpdate和updated里面 info的值都变为了'rewrite'

created和mounted区别

通过上面的说明可以理解两者的不同。通常created使用的次数多,而mounted通常是在html渲染完成后去获取或者更新某些DOM元素时使用,在mounted中通常会对插件或者组件进行使用。

var vm = new Vue({
el: '#vue_det',
template: "<ul><li id='name'>{{name}}</li><li>{{age}}</li></ul>",
created: function() {
this.name = "js"
this.age = "12"
var x = document.getElementById("name") //第一个命令台错误,这个时候模板页面还没有渲染完成
console.log(x.innerHTML); // Cannot read property 'innerHTML' of null
},
mounted: function() {
var x = document.getElementById("name") //第二个命令台输出的结果
console.log(x.innerHTML); //js
}
})

在created的时候,视图中的html并没有渲染出来,所以此时如果直接去操作html的dom节点,一定找不到相关的元素,而在mounted中,由于此时html已经渲染出来了,所以可以直接操作dom节点,故输出了结果“js”。

el选项的有无对生命周期过程的影响

系统会判断对象中有没有el选项,有el选项,则继续编译过程,没有el选项,则停止编译,也意味着暂时停止了生命周期,直到vm.$mount(el)

var vm = new Vue({
el: '#vue_det',
data: {
info: "This Test"
},
beforeCreate: function() {
console.log('调用了beforeCreat钩子函数')
},
created: function() {
console.log('调用了created钩子函数')
},
beforeMount: function() {
console.log('调用了beforeMount钩子函数')
},
mounted: function() {
console.log('调用了mounted钩子函数')
}
})
//控制台输出
/*
*调用了beforeCreat钩子函数
*调用了created钩子函数
*调用了beforeMount钩子函数
*调用了mounted钩子函数
*/

在el选项填写且正确的时候,生命周期将正常进行。

下面我们去掉el选项:

var vm = new Vue({
data: {
info: "This Test"
},
beforeCreate: function() {
console.log('调用了beforeCreat钩子函数')
},
created: function() {
console.log('调用了created钩子函数')
},
beforeMount: function() {
console.log('调用了beforeMount钩子函数')
},
mounted: function() {
console.log('调用了mounted钩子函数')
}
})
//控制台输出
/*
*调用了beforeCreat钩子函数
*调用了created钩子函数
*/

可以看到,生命周期的钩子函数执行到created就结束了。

而当我们不加el选项,但是手动执行vm.$mount(el)方法的话,也能够使暂停的生命周期进行下去,如下所示:

var vm = new Vue({
data: {
info: "This Test"
},
beforeCreate: function() {
console.log('调用了beforeCreat钩子函数')
},
created: function() {
console.log('调用了created钩子函数')
},
beforeMount: function() {
console.log('调用了beforeMount钩子函数')
},
mounted: function() {
console.log('调用了mounted钩子函数')
}
})
vm.$mount('#vue_det');
//控制台输出
/*
*调用了beforeCreat钩子函数
*调用了created钩子函数
*调用了beforeMount钩子函数
*调用了mounted钩子函数
*/

可以看到,这个时候虽然对象中没有el参数,但通过$mount(el)动态添加的方式,也能够使生命周期顺利进行。

template参数选项的有无对生命周期的影响

  • 1、如果Vue实例对象中有template参数选项,则将其作为模板编译成render函数
  • 2、如果没有template参数选项,则将外部的HTML作为模板编译(template),也就是说,template参数选项的优先级要比外部的HTML高
  • 3、如果1,2条件都不具备,则报错
<div id="vue_det">
{{info}} 这是在outer HTML中的
</div>
<script>
var vm = new Vue({
el: '#vue_det',
template: "<h1>{{info +'这是在template中的'}}</
h1>",
data: {
info: "
This Test"
},
})
</script>

执行上面的代码,输入页面显示为 This Test 这是在template中的。

render选项参数比template更接近Vue解析器!所以综合排列如下:render函数选项  > template参数  > 外部HTML;Vue的编译实际上是指Vue把模板编译成 render 函数的过程

Vue.nextTick对异步函数的结果进行操作

Vue.nextTick()官方解释:在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。

什么时候需要用的Vue.nextTick()

在Vue生命周期的created()钩子函数进行的DOM操作一定要放在Vue.nextTick()的回调函数中,因为在created()钩子函数执行的时候DOM 其实并未进行任何渲染,而此时进行DOM操作无异于徒劳,所以此处一定要将DOM操作的js代码放进Vue.nextTick()的回调函数中。

所以解释起来就是:在某个动作有可能改变DOM元素结构的时候,对DOM一系列的js操作都要放进Vue.nextTick()的回调函数中。


以上是关于浅谈vue的生命周期的主要内容,如果未能解决你的问题,请参考以下文章

关于片段生命周期

调用 replace() 时片段的生命周期是啥?

浅谈Spring中Bean的生命周期

在不存在的片段上调用片段生命周期和 onCreate 的问题

Android片段生命周期:onResume调用了两次

导航上的片段生命周期重叠