一、生命周期图示
上图为官网给出的生命周期图示,可以看到生命周期中存在八个生命周期钩子函数,分别为:
beforeCreate
created
beforeMount
mounted
beforeUpdate
updated
beforeDestory
dstroyed
从这些钩子函数的命名上就能知道到底是在什么阶段被调用了。接下来,我们根据图示和代码演示来具体学习这八个钩子函数。
二、生命周期钩子函数
特别注意!!
所有的生命周期钩子自动绑定
this
上下文到实例中,因此你可以访问数据,对 property 和方法进行运算。这意味着你不能使用箭头函数来定义一个生命周期方法。
beforeCreate&created
之所以两个一组进行讲解,是因为这两个函数在命名和功能上都有相似之处,是一前一后的关系。
1.beforeCreate
可以从流程图看到,当我们初始化一个新的Vue实例之后,在数据观测和事件配置之前被调用。这个看以前旧的一个流程图会更清楚。
简而言之的话就是整个页面创建之前所调用的生命周期函数。
2.created
从流程图看到,created是在实例创建完成后被调用,已经完成了数据的观测,和事件的回调等工作。
创建实例完成后,要进行判断,首先看有没有el
选项,如果有的话继续向下进行编译,如果没有el
选项则停止,直到vm.$mount(el)被调用。
我们先来写一个简单的模版
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>生命周期</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<h1>{{msg}}</h1>
</div>
</body>
<script>
var vm = new Vue({
el: \'#app\',
data: {
msg: \'Vue生命周期\'
},
beforeCreate: function(){
console.log("-----beforeCreate-----"),
console.log("el:" + this.$el),
console.log("data:"+ this.$data),
console.log("msg:"+ this.msg)
},
created: function(){
console.log("-----create------"),
console.log("el:" + this.$el),
console.log("data:"+ this.$data),
console.log("msg:"+ this.msg)
},
})
</script>
</html>
可以看到,在beforeCreate函数中,el,data,msg都没有定义,也就是说仅仅只是初始化了实例,还没有创建页面。
而在create函数中,el仍然未定义,但是data和msg已经被绑定。接下来我们再写一个beforeMount函数:
beforeMount: function(){
console.log("-----beforeMount------"),
console.log("el:" + this.$el),
console.log("data:"+ this.$data),
console.log("msg:"+ this.msg)
}
同时将el选项注释掉,看下console控制台的输出。
未注释el
时
可以看到还是正常向下编译,然后执行beforeMount函数。
注释掉el
后
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>生命周期</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<h1>{{msg}}</h1>
</div>
</body>
<script>
var vm = new Vue({
// el: \'#app\',
data: {
msg: \'Vue生命周期\'
},
beforeCreate: function(){
console.log("-----beforeCreate-----"),
console.log("el:" + this.$el),
console.log("data:"+ this.$data),
console.log("msg:"+ this.msg)
},
created: function(){
console.log("-----create------"),
console.log("el:" + this.$el),
console.log("data:"+ this.$data),
console.log("msg:"+ this.msg)
},
beforeMount: function(){
console.log("-----beforeMount------"),
console.log("el:" + this.$el),
console.log("data:"+ this.$data),
console.log("msg:"+ this.msg)
}
})
</script>
</html>
从图上我们可以看到,执行完create函数后,发现没有el
选项了,然后便停了下来,等待vm.$mount(el)被调用才能继续向下执行,所以才会出现页面上只有msg,不会有Vue生命周期
字样了。因为这里只是data和msg被绑定了值,但是还没有通过模版编译渲染到页面上。
3.el
-
类型:
string | Element
-
限制:只在用
new
创建实例时生效。 -
详细:
提供一个在页面上已存在的 DOM 元素作为 Vue 实例的挂载目标。可以是 CSS 选择器,也可以是一个 HTMLElement 实例。
在实例挂载之后,元素可以用
vm.$el
访问。如果在实例化时存在这个选项,实例将立即进入编译过程,否则,需要显式调用
vm.$mount()
手动开启编译。
显式调用 vm.$mount()
手动开启编译:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>生命周期</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<h1>{{msg}}</h1>
</div>
</body>
<script>
var vm = new Vue({
// el: \'#app\', //这边注释掉el,如果不进行vm.$mount则不会进入编译过程
data: {
msg: \'Vue生命周期\'
},
beforeCreate: function(){
console.log("-----beforeCreate-----"),
console.log("el:" + this.$el),
console.log("data:"+ this.$data),
console.log("msg:"+ this.msg)
},
created: function(){
console.log("-----create------"),
console.log("el:" + this.$el),
console.log("data:"+ this.$data),
console.log("msg:"+ this.msg)
},
beforeMount: function(){
console.log("-----beforeMount------"),
console.log("el:" + this.$el),
console.log("data:"+ this.$data),
console.log("msg:"+ this.msg)
}
});
vm.$mount("#app"); //显式调用,进行实例挂载
</script>
</html>
beforeMount&mounted
前面说到执行完created函数后先判断有没有el选择,有的话则再判断是否有无template选择,有的话则将其作为模版编译成render函数,如果没有则将外部HTML作为模版编译。这里也体现了template中模版优先级较高。
接下来就到了beforeMount函数了,该函数在挂载开始之前被调用。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>生命周期</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<h1>{{msg}}</h1>
</div>
</body>
<script>
var vm = new Vue({
el: \'#app\',
data: {
msg: \'Vue生命周期\'
},
beforeMount: function(){
console.log("-----beforeMount------"),
console.log("el:" + this.$el),
console.log(this.$el);
console.log("data:"+ this.$data),
console.log("msg:"+ this.msg)
},
mounted: function(){
console.log("-----mount------"),
console.log("el:" + this.$el),
console.log(this.$el);
console.log("data:"+ this.$data),
console.log("msg:"+ this.msg)
}
});
</script>
</html>
我们通过在挂载前和挂载后的钩子函数,看看到底有什么变化。
我们不难发现,在beforeMount函数执行中,页面上的msg仍然没有被挂载到页面,还是以占位符的形式存在于页面,而在mounted钩子函数中可以看到msg已经被正确挂载了。从流程图来看
在这个挂载的过程中创建了vm.\\(el,并且替代了之前的el。 实例被挂载后调用,这时 `el` 被新创建的 `vm.\\)el
替换了。如果根实例挂载到了一个文档内>的元素上,当
mounted被调用时
vm.\\(el` 也在文档内。 注意 `mounted` **不会**保证所有的子组件也都一起被挂载。如果你希望等到整个视图都渲染>完毕,可以在 `mounted` 内部使用 [vm.\\)nextTick]
mounted: function () {
this.$nextTick(function () {
// Code that will run only after the
// entire view has been rendered
})
}
beforeUpdate&updated
这里我们仍然需要通过流程图来看具体的执行过程。
在已挂载的情况下,如果data中的数据发生了改变,会触发相应组件的重新渲染,也就是相继地调用beforeUpdate和updated函数。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>生命周期</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<h1>{{msg}}</h1>
</div>
</body>
<script>
var vm = new Vue({
el: \'#app\',
data: {
msg: \'Vue生命周期\'
},
beforeUpdate: function(){
console.log("-----beforeUpdate------"),
console.log("el:" + this.$el.innerHTML),
console.log(this.$el);
console.log("data:"+ this.$data),
console.log("msg:"+ this.msg)
},
updated: function(){
console.log("-----updated------"),
console.log("el:" + this.$el.innerHTML),
console.log(this.$el);
console.log("data:"+ this.$data),
console.log("msg:"+ this.msg)
},
});
</script>
</html>
我们来到控制台输入
vm.msg="更新一下"
然后回车。
这边有一个问题就是代码中我们需要使用Inner HTML属性来查看,因为这里涉及到了虚拟DOM的问题,具体可以参考https://segmentfault.com/q/1010000011521681这篇文章。也就是说我们要查看的是其真实DOM中的内容,所以这里在对数据进行重新渲染的过程中是有一个变化的。
beforeDestroy&destroyed
这两个钩子函数是在vm.$destroy()被调用时才会执行。在这个过程中将会销毁实例,即Vue实例所有绑定的指令都会解绑,事件监听器、子组件等都会被移除和销毁。
三、总结
这篇文章主要就生命周期图示学习了一下在生命周期中各个钩子函数所处的阶段,以及在这一过程中的一系列变化。仅做学习参考,同时在写的过程中不免会有问题,欢迎大家讨论!