Vue3-组件生命周期
一、前言
本质上,每个主要Vue生命周期事件被分成两个钩子,分别在事件之前和之后调用。Vue应用程序中有4个主要事件(8个主要钩子)。
- 创建 — 在组件创建时执行
- 挂载 — DOM 被挂载时执行
- 更新 — 当响应数据被修改时执行
- 销毁 — 在元素被销毁之前立即运行
Vue3生命周期图
Vue2到Vue3生命周期钩子变化
这个从Vue2 到Vue3的生命周期映射是直接从Vue 3 Composition API文档中获得的:
beforeCreate-> 使用setup()created-> 使用setup()beforeMount->onBeforeMountmounted->onMountedbeforeUpdate->onBeforeUpdateupdated->onUpdatedbeforeDestroy->onBeforeUnmountdestroyed->onUnmountederrorCaptured->onErrorCaptured
二、在选项API中使用Vue3生命周期钩子
使用 选项API,生命周期钩子是被暴露 Vue实例上的选项。我们不需要导入任何东西,只需要调用这个方法并为这个生命周期钩子编写代码。
例如,假设我们想访问mounted()和updated()生命周期钩子,可以这么写:
javascript复制代码// 选项 API <script> export default { mounted() { console.log(''mounted!'') }, updated() { console.log(''updated!'') } } </script>
三、在组合API中使用Vue3生命周期钩子
在组合API中,我们需要将生命周期钩子导入到项目中,才能使用,这有助于保持项目的轻量性。
javascript复制代码// 组合 API import { onMounted } from ''vue''
除了beforecate和created(它们被setup方法本身所取代),我们可以在setup方法中访问的API生命周期钩子有9个选项:
onBeforeMount– 在挂载开始之前被调用:相关的render函数首次被调用。onMounted– 组件挂载时调用onBeforeUpdate– 数据更新时调用,发生在虚拟 DOM 打补丁之前。这里适合在更新之前访问现有的 DOM,比如手动移除已添加的事件监听器。onUpdated– 由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子。- onBeforeUnmount – 在卸载组件实例之前调用。在这个阶段,实例仍然是完全正常的。
onUnmounted– 卸载组件实例后调用。调用此钩子时,组件实例的所有指令都被解除绑定,所有事件侦听器都被移除,所有子组件实例被卸载。onActivated– 被keep-alive缓存的组件激活时调用。onDeactivated– 被keep-alive缓存的组件停用时调用。onErrorCaptured– 当捕获一个来自子孙组件的错误时被调用。此钩子会收到三个参数:错误对象、发生错误的组件实例以及一个包含错误来源信息的字符串。此钩子可以返回false以阻止该错误继续向上传播。
使用示例
javascript复制代码// 组合 API <script> import { onMounted } from ''vue'' export default { setup () { onMounted(() => { console.log(''mounted in the composition api!'') }) } } </script>
四、深入探索生命周期钩子
我们深入探索每个生命周期钩子,看看它们是如何被使用的,我们可以在每个钩子中编写特定代码,来测试在Options API和Composition API中的各自的区别。
beforeCreate() – 选项 API
由于创建的挂钩是用于初始化所有响应数据和事件的事物,因此beforeCreate无法访问组件的任何响应数据和事件。
以下面的代码块为例:
javascript复制代码// 选项 API export default { data() { return { val: ''hello'' } }, beforeCreate() { console.log(''Value of val is: '' + this.val) } }
val的输出值是 undefined,因为尚未初始化数据,我们也不能在这调用组件方法。
如果你想查看可用内容的完整列表,建议只运行console.log(this)来查看已初始化的内容。当使用选项API时,这做法在其他钩子中也很有用。
created() – 选项 API
如果我们要在组件创建时访问组件的数据和事件,可以把上面的 beforeCreate 用 created代替。
javascript复制代码// 选项API export default { data() { return { val: ''hello'' } }, created() { console.log(''Value of val is: '' + this.val) } }
其输出为Value of val is: hello,因为我们已经初始化了数据。
在处理读/写反应数据时,使用created 的方法很有用。 例如,要进行API调用然后存储该值,则可以在此处进行此操作。
最好在这里执行此操作,而不是在mounted 中执行此操作,因为它发生在Vue的同步初始化过程中,并且我们需要执行所有数据读取/写入操作。
setup() – 组合式
对于使用 组合API 的 Vue3 生命周期钩子,使用setup()方法替换beforecate和created。这意味着,在这些方法中放入的任何代码现在都只在setup方法中。
javascript复制代码// 组合AP import { ref } from ''vue'' export default { setup() { const val = ref(''hello'') console.log(''Value of val is: '' + val.value) return { val } } }
beforeMount() 和 onBeforeMount()
在组件DOM实际渲染安装之前调用。在这一步中,根元素还不存在。在选项API中,可以使用this.$els来访问。在组合API中,为了做到这一点,必须在根元素上使用ref。
javascript复制代码// 选项 API export default { beforeMount() { console.log(this.$el) } }
组合API中使用 ref:
javascript复制代码// 组合 API <template> <div ref=''root''> Hello World </div> </template> 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。
mounted() and onMounted()
在组件的第一次渲染后调用,该元素现在可用,允许直接DOM访问
同样,在 选项API中,我们可以使用this.$el来访问我们的DOM,在组合API中,我们需要使用refs来访问Vue生命周期钩子中的DOM。
javascript复制代码import { ref, onMounted } from ''vue'' export default { setup() { /* 组合 API */ const root = ref(null) onMounted(() => { console.log(root.value) }) return { root } }, mounted() { /* 选项 API */ console.log(this.$el) } }
beforeUpdate() and onBeforeUpdate()
数据更新时调用,发生在虚拟 DOM 打补丁之前。这里适合在更新之前访问现有的 DOM,比如手动移除已添加的事件监听器。
beforeUpdate对于跟踪对组件的编辑次数,甚至跟踪创建“撤消”功能的操作很有用。
updated() and onUpdated()
DOM更新后,updated的方法即会调用。
javascript复制代码<template> <div> <p>{{val}} | edited {{ count }} times</p> <button @click=''val = Math.random(0, 100)''>Click to Change</button> </div> </template>
选项 API 方式:
javascript复制代码export default { data() { return { val: 0 } }, beforeUpdate() { console.log("beforeUpdate() val: " + this.val) }, updated() { console.log("updated() val: " + this.val } }
组合API的方式:
javascript复制代码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 } } }
这些方法很有用,但是对于更多场景,我们需要使用的
watch方法检测这些数据更改。watch之所以好用,是因为它给出了更改后的数据的旧值和新值。另一种选择是使用计算属性来基于元素更改状态。
beforeUnmount() 和 onBeforeUnmounted()
在卸载组件实例之前调用。在这个阶段,实例仍然是完全正常的。
在 选项 API中,删除事件侦听器的示例如下所示。
javascript复制代码export default { mounted() { console.log(''mount'') window.addEventListener(''resize'', this.someMethod); }, beforeUnmount() { console.log(''unmount'') window.removeEventListener(''resize'', this.someMethod); }, methods: { someMethod() { // do smth } } } // 组合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() 和 onUnmounted()
卸载组件实例后调用。调用此钩子时,组件实例的所有指令都被解除绑定,所有事件侦听器都被移除,所有子组件实例被卸载。
javascript复制代码import { onUnmounted } from ''vue'' export default { setup () { /* 组合 API */ onUnmounted(() => { console.log(''unmounted'') }) }, unmounted() { /* 选项 API */ console.log(''unmounted'') } }
activated() and onActivated()
被 keep-alive 缓存的组件激活时调用。
例如,如果我们使用keep-alive组件来管理不同的选项卡视图,每次在选项卡之间切换时,当前选项卡将运行这个 activated 钩子。
假设我们使用keep-alive包装器进行以下动态组件。
javascript复制代码<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 () { /* 组合 API */ const tabName = ref(''Tab1'') return { tabName } } } </script>
在Tab1.vue组件内部,我们可以像这样访问activated钩子。
javascript复制代码<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() 和 onDeactivated()
被 keep-alive 缓存的组件停用时调用。
这个钩子在一些用例中很有用,比如当一个特定视图失去焦点时保存用户数据和触发动画。
javascript复制代码import { onActivated, onDeactivated } from ''vue'' export default { setup() { onActivated(() => { console.log(''Tab 1 Activated'') }) onDeactivated(() => { console.log(''Tab 1 Deactivated'') }) } }
现在,当我们在选项卡之间切换时,每个动态组件的状态都将被缓存和保存。
Vue3调试钩子
Vue3 为我们提供了两个可用于调试目的的钩子。
onRenderTrackedonRenderTriggered
这两个事件都带有一个debugger event,此事件告诉你哪个操作跟踪了组件以及该操作的目标对象和键。
onRenderTracked
跟踪虚拟 DOM 重新渲染时调用。钩子接收 debugger event 作为参数。此事件告诉你哪个操作跟踪了组件以及该操作的目标对象和键。
javascript复制代码<div id="app"> <button v-on:click="addToCart">Add to cart</button> <p>Cart({{ cart }})</p> </div> const app = Vue.createApp({ data() { return { cart: 0 } }, renderTracked({ key, target, type }) { console.log({ key, target, type }) /* 当组件第一次渲染时,这将被记录下来: { key: "cart", target: { cart: 0 }, type: "get" } */ }, methods: { addToCart() { this.cart += 1 } } }) app.mount(''#app'')
renderTracked
当虚拟 DOM 重新渲染为 triggered.Similarly 为renderTracked,接收 debugger event 作为参数。此事件告诉你是什么操作触发了重新渲染,以及该操作的目标对象和键。
用法:
javascript复制代码<div id="app"> <button v-on:click="addToCart">Add to cart</button> <p>Cart({{ cart }})</p> </div> typescript复制代码const app = Vue.createApp({ data() { return { cart: 0 } }, renderTriggered({ key, target, type }) { console.log({ key, target, type }) }, methods: { addToCart() { this.cart += 1 /* 这将导致renderTriggered调用 { key: "cart", target: { cart: 1 }, type: "set" } */ } } }) app.mount(''#app'')