原文地址:(A Complete Guide to Vue Lifecycle Hooks in Vue3 – LearnVue)
Vue3与Vue2的生命周期钩子(hooks)非常相似——我们仍可以在相同的场景下使用相同(或类似)的钩子。
如果项目使用Options API方式构建,那就无须修改任何生命周期相关的代码。这是因为Vue3在设计时已对先前版本做了兼容处理。
然而,如果使用Composition API方式(构建大型Vue项目时非常有用)构建项目,生命周期钩子则会略有不同。
通过阅读本文,你将了解如何在Options API和Composition API中使用生命周期钩子,并且让你书写更优秀的代码。
冲!
什么是Vue的生命周期钩子
首先,让我们看一下Options API和Composition API的Vue3生命周期钩子示意图。在我们深入了解之前,能有一个整体上的概念。
基本上,每个主要的(main)Vue生命周期事件会对应2个钩子函数,分别在事件开始前和事件结束后调用。在Vue app中你可以应用4个主要事件(8个主要钩子(main hooks)函数)。
- Creation ——组件创建时运行
- Mounting ——挂载在DOM时运行
- Updates ——响应数据修改时运行
- Destruction ——元素销毁前运行
在Options API中使用生命周期钩子
使用Options API时,生命周期钩子暴露在Vue实例的选项中。不需要我们导入任何东西,只需调用对应的函数并书写相关生命周期的代码即可。
举个例子,假设我们想要访问mounted()
和updated()
生命周期钩子,代码可能如下所示:
<script>
export default {
mounted() {
console.log('mounted!')
},
updated() {
console.log('updated!')
}
}
</script>
是不是非常简单?
那么,接下来我们继续在Composition API中使用Vue3的生命周期钩子。
在Composition API中使用生命周期钩子
在Composition API中,我们需要先导入对应的生命周期钩子才能使用它。这么做,是为了让我们的项目尽可能的轻量化。
import { onMounted } from 'vue'
除了beforeCreate
和created
(被setup
方法本身代替),共有9个Options API生命周期相对应的方法可以在setup中使用。
- onBeforeMount——挂载开始前调用
- onMount——挂载后调用
- onBeforeUpdate——当响应数据改变,且重新渲染前调用
- onUpdated——重新渲染后调用
- onBeforeUnmount——Vue实例销毁前调用
- onUnmounted——实例销毁后调用
- onActivated——当keep-alive组件被激活时调用
- onDeactivated——当keep-alive组件取消激活时调用
- onErrorCaptured——从子组件中捕获错误时调用
当我们需要导入并访问这些钩子函数时,代码可能如下所示:
<script>
import { onMounted } from 'vue'
export default {
setup () {
onMounted(() => {
console.log('mounted in the composition api!')
})
}
}
</script>
将Vue2代码更新为Vue3生命周期钩子
你可以方便的在 Vue3 Composition API docs中查看Vue2到Vue3生命周期的映射。并且,我认为这也是明确如何改变和使用它们的最有效的方式之一。
- beforeCreate -> setup()
- created -> setup()
- beforeMount -> onBeforeMount
- mounted -> onMounted
- beforeUpdate -> onBeforeUpdate
- updated -> onUpdated
- beforeDestroy -> onBeforeUnmount
- destroyed -> onUnmounted
- errorCaptured -> onErrorCaptured
深入了解每个生命周期钩子
现在我们已知两个重要的事情:
- 我们能够使用的各种生命周期
- 如何在Options API和Composition API中使用它们
接下来,让我们深入每个生命周期钩子中,了解如何使用它们,各个钩子中可以写什么样的代码,并且聊聊介于Options API和Composition API中的不同。
Creation钩子——VueJS生命周期的起始
Creation钩子是你启动项目时触发的第一个事件。
beforeCreate() ——Options API
因为created
钩子是用来初始化响应数据和事件的,所以你不能在beforeCreate
钩子里访问任何组件的响应式数据和事件。
观察以下代码示例:
export default {
data() {
return {
val: 'hello'
}
},
beforeCreate() {
console.log('Value of val is: ' + this.val)
}
}
因为此时数据还未初始化,所以val
的值是undefined
,并且你也不能在此函数中调用组件中的其他方法。
如果你想要查看能使用的属性列表,我建议通过console.log(this)
查看哪些属性已经初始化。这对Options API的其他钩子函数也同样有效。
beforeCreate
对那些不需要分配数据(data)的逻辑和API调用来说十分有用。如果我们此时对数据(data)赋值,那么这些值将在状态初始化后丢失。
created() – Options API
此时,我们已经可以访问组件的数据和事件。因此,将上例中的beforeCreate
替换为created,我们能够看到输出有的改变。
export default {
data() {
return {
val: 'hello'
}
},
created() {
console.log('Value of val is: ' + this.val)
}
}
因为此时已经初始化,所以上例将输出 Value of val is: hello
。
当需要处理响应式数据读/写(reading/writing)时created方法非常有用。举个例子,如果你需要完成一个API调用并存储它的值,那么你应该将它写在这里。
这将比在mounted中处理来的更好,因为它在Vue的初始化进程中更早触发,并且你也能读写所有的数据。
那Composition API的Creation钩子呢?
在Composition API的生命周期钩子中,beforeCreate
和created
被setup()
方法代替。这意味着你应该将对应的代码写在setup方法中。
将刚刚created生命周期钩子重写为如下所示
import { ref } from 'vue'
export default {
setup() {
const val = ref('hello')
console.log('Value of val is: ' + val.value)
return {
val
}
}
}
Mounting钩子——访问DOM
mounting钩子处理组件的挂载和渲染。这是我们在项目和应用程序中最常用的一组钩子。
beforeMount() and onBeforeMount()
在组件DOM实际渲染和挂载前触发。在此阶段,根元素(root element)还未存在。在Options API中,可以通过this.$el
访问。而在Composition API中,你必需通过ref
来指定根元素。
export default {
beforeMount() {
console.log(this.$el)
}
}
Composition代码可能如下所示:
<template>
<div ref='root'>
Hello World
</div>
</template>
通过ref访问的script代码如下:
import { ref, onBeforeMount } from 'vue'
export default {
setup() {
const root = ref(null)
onBeforeMount(() => {
console.log(root.value)
})
return {
root
}
},
beforeMount() {
console.log(this.$el)
}
}
因为此时app.$el
还未创建,上述代码的输出将是undefined。
于此同时,我们更推荐在created()
和setup
中执行你的API调用。两者具有相同的组件变量访问,但是beforeMount会在created之后调用。
mounted() and onMounted()
在组件第一次渲染时调用。此时,组件已经可以访问DOM。
同样,在Options API中,我们使用this.$el
访问我们的DOM,而在Composition API的生命周期钩子中,我们需要借助refs去访问相应DOM。
import { ref, onMounted } from 'vue'
export default {
setup() { /* Composition API */
const root = ref(null)
onMounted(() => {
console.log(root.value)
})
return {
root
}
},
mounted() { /* Options API */
console.log(this.$el)
}
}
Update钩子——VueJS生命周期中的响应
无论何时响应数据被修改,updated生命周期事件都将触发,并且触发渲染更新。
beforeUpdate() and onBeforeUpdate()
在数据修改且组件重渲染之前执行。这是任何更改还未发生前,手动修改DOM的好地方。
beforeUpdate
对追踪组件的编辑次数时非常有用,甚至可以通过追踪对应的操作来创建一个“撤销”功能。
updated() and onUpdated()
updated方法在DOM更新后调用一次。这是一段beforeUpdate和updated的基本代码
<template>
<div>
<p>{{val}} | edited {{ count }} times</p>
<button @click='val = Math.random(0, 100)'>Click to Change</button>
</div>
</template>
相对应的script代码
export default {
data() {
return {
val: 0
}
},
beforeUpdate() {
console.log("beforeUpdate() val: " + this.val)
},
updated() {
console.log("updated() val: " + this.val
}
}
或
import { ref, onBeforeUpdate, onUpdated } from 'vue'
export default {
setup () {
const count = ref(0)
const val = ref(0)
onBeforeUpdate(() => {
count.value++;
console.log("beforeUpdate");
})
onUpdated(() => {
console.log("updated() val: " + val.value)
})
return {
count, val
}
}
}
这些方法很有用,但多数情况我们可能会通过监听器(watchers)去检测对应数据的改变。因为监听器可以很好的提供数据更改时的旧值和新值。
另一种方式是通过计算属性来改变元素的状态。
Destruction钩子——清理
destruction钩子在组件被移除并需要清理一些待释放的功能时使用。这是删除事件监听并且防止内存溢出的好地方。
beforeUnmount() and onBeforeUnmounted()
触发在组件开始销毁之前,在此会进行绝大多数的清理工作。在此阶段,你的组件仍然拥有所有的功能,任何东西都还未被销毁。
举一个删除事件监听的例子,Options API方式如下
export default {
mounted() {
console.log('mount')
window.addEventListener('resize', this.someMethod);
},
beforeUnmount() {
console.log('unmount')
window.removeEventListener('resize', this.someMethod);
},
methods: {
someMethod() {
// do smth
}
}
}
Composition API方式
import { onMounted, onBeforeUnmount } from 'vue'
export default {
setup () {
const someMethod = () => {
// do smth
}
onMounted(() => {
console.log('mount')
window.addEventListener('resize', someMethod);
})
onBeforeUnmount(() => {
console.log('unmount')
window.removeEventListener('resize', someMethod);
})
}
}
可以在Vite,vue-cli,或任何支持热加载的环境中查看这些行为的执行。当你更新代码时,你的组件会销毁,并重新挂载。
unmounted() and onUnmounted()
此时,大多数组件和它的属性已经销毁,所以你能做的不多。同样,我打印一段数据去确切观察发生了什么。
import { onUnmounted } from 'vue'
export default {
setup () { /* Composition API */
onUnmounted(() => {
console.log('unmounted')
})
},
unmounted() { /* Options API */
console.log('unmounted')
}
}
Activation钩子——管理Keep-Alive组件
keep-alive标签是对动态组件的包装元素。它保存一段组件实例的缓存引用,如此Vue就不需要在每次动态组件(dynamic component)改变时创建一个新的实例。
对于这种特殊的使用情况,Vue提供了两个生命周期钩子。
activated() and onActivated()
无论何时动态组件被“重新激活”(意味着此时是动态组件激活视图)时该钩子方法都将被调用。
举个例子,如果我们使用keep-alive管理不同的tab视图,每次我们切换tab时,当前的tab将会触发activated钩子。
假设我们有以下用keep-alive包装的 动态组件setup。
<template>
<div>
<span @click='tabName = "Tab1"'>Tab 1 </span>
<span @click='tabName = "Tab2"'>Tab 2</span>
<keep-alive>
<component :is='tabName' class='tab-area'/>
</keep-alive>
</div>
</template>
<script>
import Tab1 from './Tab1.vue'
import Tab2 from './Tab2.vue'
import { ref } from 'vue'
export default {
components: {
Tab1,
Tab2
},
setup () { /* Composition API */
const tabName = ref('Tab1')
return {
tabName
}
}
}
</script>
在我们的Tab1.vue
组件中,我们能够如下访问activation钩子。
<template>
<div>
<h2>Tab 1</h2>
<input type='text' placeholder='this content will persist!'/>
</div>
</template>
<script>
import { onActivated } from 'vue'
export default {
setup() {
onActivated(() => {
console.log('Tab 1 Activated')
})
}
}
</script>
deactivated() and onDeactivated()
和你想象的一样,视图不在动态组件中继续保持激活时触发此钩子。
该钩子在某些情况下非常有用,例如特定视图失去焦点时,保存用户数据和触发动画。
我们能够如下所示,捕获该生命周期钩子
import { onActivated, onDeactivated } from 'vue'
export default {
setup() {
onActivated(() => {
console.log('Tab 1 Activated')
})
onDeactivated(() => {
console.log('Tab 1 Deactivated')
})
}
}
现在,当我们切换tab时,每个动态组件的状态都将被保存。
好耶!
Vue3 Debug Hooks
Vue3为我们提供了两个调试时的钩子:
onRenderTracked
onRenderTriggered
这两个事件都携带一个DebuggerEvent参数,以允许我们获悉是什么触发了Vue实例的重新渲染。
export default {
onRenderTriggered(e) {
debugger
// 检测什么依赖造成了组件的重新渲染
}
}
总结
无论是使用Options API还是Composition API,不能仅知道你能用什么生命周期,还要知道为什么使用它们。
许多问题都能在多个生命周期钩子中解决。但是,最好知道哪个生命周期是最适合你当下的情况。不管怎样,你都需要深思熟虑,并对使用特定的生命周期有充足的理由。
我希望借此文章对你理解生命周期函数,并且在你项目中应用它们时有所帮助。
Happy coding!
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!