在之前的 Vue 学习中,我们在使用 Vue 时,都会创建一个 Vue 的实例,而每个 Vue 实例在被创建时都要经过一系列的初始化过程。例如,需要设置数据监听、编译模板、将实例挂载到 DOM 并在数据变化时更新 DOM 等。在这个过程中,Vue 会运行一些生命周期钩子函数,而我们则可以在钩子函数中编写一些自定义方法,用以在 Vue 的整个生命周期中某些阶段实现我们特殊需求。那么,本章,我们就来了解 Vue 实例的生命周期钩子函数。
学习系列目录地址:https://www.cnblogs.com/danvic712/p/9549100.html
仓储地址:https://github.com/Lanesra712/VueTrial/blob/master/Chapter01-Rookie/hocks.html
在我们使用 Vue 的时候,都会先创建一个 Vue 实例,这个实例不仅是我们挂载 Vue 框架的入口,也是 MVVM 思想中的 VM(ViewModel)。在我们使用 Vue 的整个过程中,归根结底都是在对这个 Vue 实例进行操作。因此,只有当我们了解 Vue 实例的生命周期之后,才可以更好的实现我们的业务逻辑。
在 Vue 官网的这张生命周期图示中,我们可以看到整个生命周期中包含了8个钩子函数:beforeCreate、created、beforeMount、mounted、beforeUpdate、updated、beforeDestroy、destroyed。从字面意思我们就可以清楚的看出来,这8个钩子函数两个一组,分别对应于 Vue 实例的创建、挂载、更新、销毁,接下来,我们就根据这四个阶段,解释 Vue 实例各个阶段中的钩子函数的作用。
在我们通过 new Vue() 创建了一个 Vue 实例之后,会执行 init 方法,此时只会初始化 Vue 实例所包含的一些默认的事件与生命周期函数,在这个实例还未被完全创建之前,则会执行我们的 beforeCreate 钩子函数。
在下面的例子中,我们在实例化 Vue 对象时,自定义了一个 message 属性,同时设定了一个 show 方法,现在我们来看看当实例并没有完全被创建之前,是否能够获取到我们自定义的属性与方法。
<div id="app">
{{message}}
</div>
<script>
var vm = new Vue({
el: '#app',
data: {
message: 'Hello World!'
},
methods: {
show() {
console.log('执行了 show 方法');
}
},
beforeCreate() {
console.log(`Vue 实例挂载对象 el:${this.$el}`)
console.log(`Vue 实例的 data 对象:${this.$data}`)
console.log(`Vue 实例的 message 属性值:${this.message}`)
console.log(`Vue 实例的 methods 对象:${this.$options.methods}`)
this.show();
}
})
</script>
从浏览器的控制台中我们可以看到,此时,Vue 实例中的 data、methods 对象,或是我们定义的 Vue 实例的挂载点元素,在 beforeCreated 生命周期钩子函数执行时,都没有进行了初始化。
当 beforeCreated 钩子函数执行完成后,Vue 实例已经初始化完成,此时将要执行生命周期中的 created 钩子函数来监听我们对于数据的更改或是监听事件。
<div id="app">
{{message}}
</div>
<script>
var vm = new Vue({
el: '#app',
data: {
message: 'Hello World!'
},
methods: {
show() {
console.log('执行了 show 方法');
}
},
created() {
console.log(`Vue 实例挂载对象 el:${this.$el}`)
console.log(`Vue 实例的 data 对象:${this.$data}`)
console.log(`Vue 实例的 message 属性值:${this.message}`)
console.log(`Vue 实例的 methods 对象:${this.$options.methods}`)
this.show();
}
})
</script>
从浏览器控制台打印出的信息可以看出,在执行 created 钩子函数的过程中,对于自定义的属性 message、自定义的方法 show 已经初始化完成,此时,整个 Vue 实例已经初始化完成。但是,对于我们的 Vue 实例的挂载点元素还没有进行初始化。也就是说,当执行完 created 之后,Vue 实例与 View(视图层)之间依旧是处于隔离的状态,初始化完成的 Vue 实例也依旧没有与 DOM 进行绑定。
当 Vue 实例执行完 beforeCreated、created 钩子函数之后,Vue 实例已经初始化完成,而 Vue 实例并没有挂载到页面的 DOM 上。在挂载到页面 DOM 元素之前,则需要执行 beforeMount 钩子函数将我们的实例绑定到模板上进行编译渲染。
<div id="app">
<h3 id="h3">{{message}}</h3>
</div>
<script>
var vm = new Vue({
el: '#app',
data: {
message: 'Hello World!'
},
methods: {
show() {
console.log('执行了 show 方法');
}
},
beforeMount() {
console.log(document.getElementById('h3').innerText)
}
})
</script>
从控制台输出的信息可以看到,当执行到 beforeMount 钩子函数时,已经将模板编译完成,但是尚未挂载到页面上去。
当把编译完成的模板挂载到页面上时,则需要执行 mounted 钩子函数,在这个阶段,用户就可以看到已经渲染好的页面。
<div id="app">
<h3 id="h3">{{message}}</h3>
</div>
<script>
var vm = new Vue({
el: '#app',
data: {
message: 'Hello World!'
},
methods: {
show() {
console.log('执行了 show 方法');
}
},
mounted() {
console.log(document.getElementById('h3').innerText)
}
})
</script>
这里我们可以看到,已经可以获取到差值表达式的值,即当执行到 mounted 钩子函数时,页面已经渲染完成了。
从上面的例子中可以看出,mounted 是创建 Vue 实例过程中的最后一个生命周期钩子函数,当执行完 mounted 钩子函数之后,实例已经被完成创建好,并已经渲染到页面中,此时,如果我们不对实例进行任何的操作的话,Vue 实例则不会执行 新的生命周期钩子函数。
在执行完了 mounted 钩子函数之后,Vue 实例实际已经脱离了实例的创建阶段,进入实例的运行阶段。此时,当我们对实例的 data 进行修改时,则会触发 beforeUpdate、updated 这两个钩子函数。
<div id="app">
<h3 id="h3">{{message}}</h3>
</div>
<script>
var vm = new Vue({
el: '#app',
data: {
message: 'Hello World!'
},
methods: {
show() {
console.log('执行了 show 方法');
}
},
beforeUpdate() {
console.log(`页面上的数据:${document.getElementById('h3').innerText}`)
console.log(`data 中的 message 数据:${this.message}`)
}
})
</script>
我们可以看到,当我们对 data 中的 message 属性进行修改时,在执行 beforeUpdate 钩子函数时,页面上的数据还是旧的数据,而 data 中 message 属性已经将值修改成了最新的值(这里页面上显示的为修改后的数据则是因为执行了后面的钩子函数,将修改后的数据同步渲染到了页面上)。
Vue 作为一个具有数据双向绑定特性的框架,当我们实时修改了页面元素的值之后,肯定希望页面可以同步变更数据。而在执行 beforeUpdate 钩子函数之后,我们已经在实例中修改了数据,现在只需要重新渲染到页面就可以了,这时候,则会执行 updated 钩子函数。
<div id="app">
<h3 id="h3">{{message}}</h3>
</div>
<script>
var vm = new Vue({
el: '#app',
data: {
message: 'Hello World!'
},
methods: {
show() {
console.log('执行了 show 方法');
}
},
updated() {
console.log(`页面上的数据:${document.getElementById('h3').innerText}`)
console.log(`data 中的 message 数据:${this.message}`)
}
})
</script>
从控制台可以看到,当 updated 钩子函数执行的时候,页面和 data 中的数据已经完成了同步,都显示的是最新的数据。此时,整个页面数据实时变更的操作也已经完成了。
既然有 Vue 实例的创建,那么在我们不需要 Vue 实例的时候就需要将这个实例进行销毁。而 beforeDestroy 以及 destroyed 钩子函数则会帮我们实现这一目的。
<div id="app">
{{message}}
</div>
<script>
var vm = new Vue({
el: '#app',
data: {
message: 'Hello World!'
},
methods: {
show() {
console.log('执行了 show 方法');
}
},
beforeDestroy() {
console.log(`Vue 实例挂载对象 el:${this.$el}`)
console.log(`Vue 实例的 data 对象:${this.$data}`)
console.log(`Vue 实例的 message 属性值:${this.message}`)
console.log(`Vue 实例的 methods 对象:${this.$options.methods}`)
this.show();
},
destroyed() {
console.log(`Vue 实例挂载对象 el:${this.$el}`)
console.log(`Vue 实例的 data 对象:${this.$data}`)
console.log(`Vue 实例的 message 属性值:${this.message}`)
console.log(`Vue 实例的 methods 对象:${this.$options.methods}`)
this.show();
}
})
</script>
这里,我们手动销毁 Vue 实例,从控制台的输出内容可以看到,在 beforeDestroy 和 destroyed 钩子函数执行中,我们依旧可以获取到 Vue 实例的相关内容,可是,当我们选择更新 message 属性的值时会发现,此时,页面上显示的值并没有发生改变。原来,这里的销毁并不指代'抹去'这个 Vue 实例,而是表示将 Vue 实例与页面的 DOM 元素进行'解绑'。
钩子函数 | 描述 |
---|---|
beforeCreate | Vue 实例进行初始化,此时实例的各个组件还没有进行初始化完成,因此不能访问到 data、computed、watch、methods 上的方法和数据,同时,Vue 实例的挂载点也没有进行初始化 |
created | Vue 实例初始化完成,此时可以访问 data、computed、watch、methods 上的方法和数据,但是依旧没有进行 Vue 实例的挂载点初始化 |
beforeMount | 将实例绑定到模板并进行渲染,但并不会将实例挂载到页面上 |
mounted | 将渲染好的模板绑定到页面上,此时,Vue 实例已完全创建好 |
beforeUpdate | 数据变更时调用,在实例数据更改之前执行任何应该发生的自定义逻辑或操作 |
updated | 将 Vue 实例更新完成的数据重新渲染到内存中的虚拟 DOM 上,再将虚拟 DOM 应用到页面上 |
beforeDestroy | Vue 实例进入销毁阶段,此时实例上的 data、methods、过滤器、指令等等还是处于可用的状态,还没有真正执行销毁的过程(解除与页面 DOM 元素的绑定) |
destroyed | 实例被销毁(解除与页面 DOM 元素的绑定) |