在组合式 API 中使用生命周期钩子
生命周期钩子对于在不同阶段管理组件行为至关重要。在组合式 API 中,这些钩子的访问和使用方式与选项 API 不同。理解如何在 setup
函数中使用生命周期钩子对于有效管理组件状态、执行副作用和优化性能至关重要。本课将提供使用组合式 API 中生命周期钩子的全面指南,涵盖其目的、用法和实际示例。
理解组合式 API 中的生命周期钩子
在选项 API 中,像 mounted
、updated
和 unmounted
这样的生命周期钩子被定义为组件对象中的选项。然而,在组合式 API 中,这些钩子被导入为函数并在 setup
函数中调用。这种方法允许更组织和可重用的代码,尤其是在与可组合项结合使用时。
Composition API 中可用的主要生命周期钩子是:
onBeforeMount
: 在组件即将挂载时调用。onMounted
: 组件挂载完成后调用。onBeforeUpdate
: 组件即将更新其 DOM 之前调用。onUpdated
: 组件更新其 DOM 后调用。onBeforeUnmount
: 在组件实例卸载前立即被调用。onUnmounted
: 组件卸载后被调用。onErrorCaptured
: 当任何子组件的错误被捕获时被调用。onRenderTracked
: 组件渲染过程中被跟踪其响应式依赖时被调用。onRenderTriggered
: 当组件的响应式依赖项触发重新渲染时调用。onActivated
: 当组件作为缓存子树的一部分插入到 DOM 中时调用。主要用于与<keep-alive>
一起使用。onDeactivated
: 当组件作为缓存子树的一部分从 DOM 中移除时调用。主要用于与<keep-alive>
一起使用。
基本用法
要在 Composition API 中使用生命周期钩子,首先需要从 vue
中导入它。然后,在 setup
函数中调用钩子函数,传递一个回调函数,该函数将在对应的生命周期事件发生时执行。
<template>
<div>
<p>{{ message }}</p>
</div>
</template>
<script>
import { ref, onMounted } from 'vue';
export default {
setup() {
const message = ref('Hello, Composition API!');
onMounted(() => {
console.log('Component mounted!');
// Perform any initialization tasks here
});
return {
message
};
}
};
</script>
在这个例子中,onMounted
用于在组件挂载后向控制台记录一条消息。传递给 onMounted
的回调函数只会在初次渲染后执行一次。
示例:Mount获取数据
onMounted
的一个常见用例是在组件首次渲染时从 API 获取数据。
<template>
<div>
<p v-if="loading">Loading...</p>
<ul v-else>
<li v-for="todo in todos" :key="todo.id">{{ todo.title }}</li>
</ul>
</div>
</template>
<script>
import { ref, onMounted } from 'vue';
export default {
setup() {
const todos = ref([]);
const loading = ref(true);
onMounted(async () => {
try {
const response = await fetch('https://jsonplaceholder.typicode.com/todos');
todos.value = await response.json();
} catch (error) {
console.error('Error fetching data:', error);
} finally {
loading.value = false;
}
});
return {
todos,
loading
};
}
};
</script>
在这个示例中,onMounted
用于从模拟 API 获取待办事项列表。loading
ref 用于在数据获取过程中显示加载消息。一旦数据获取完成,todos
ref 将被更新,加载消息将被隐藏。
示例:更新 DOM
onBeforeUpdate
和 onUpdated
可用于在组件的 DOM 更新前后执行任务。例如,你可能希望在更新前后测量元素的高度,以检测尺寸变化。
<template>
<div>
<p ref="messageRef">{{ message }}</p>
<button @click="updateMessage">Update Message</button>
</div>
</template>
<script>
import { ref, onBeforeUpdate, onUpdated } from 'vue';
export default {
setup() {
const message = ref('Initial Message');
const messageRef = ref(null);
onBeforeUpdate(() => {
console.log('Before update:', messageRef.value?.offsetHeight);
});
onUpdated(() => {
console.log('After update:', messageRef.value?.offsetHeight);
});
const updateMessage = () => {
message.value = 'Updated Message';
};
return {
message,
messageRef,
updateMessage
};
}
};
</script>
在这个例子中,onBeforeUpdate
在更新前记录了 messageRef
元素的身高,而 onUpdated
在更新后记录了身高。这可以用于检测由于更新导致元素尺寸的变化。
示例:清理资源
onBeforeUnmount
和 onUnmounted
用于在组件卸载前清理资源。这对于防止内存泄漏和确保应用程序正确运行非常重要。
<template>
<div>
<p>{{ message }}</p>
</div>
</template>
<script>
import { ref, onMounted, onBeforeUnmount } from 'vue';
export default {
setup() {
const message = ref('Hello, Composition API!');
let intervalId = null;
onMounted(() => {
intervalId = setInterval(() => {
console.log('Interval running');
}, 1000);
});
onBeforeUnmount(() => {
clearInterval(intervalId);
console.log('Interval cleared');
});
return {
message
};
}
};
</script>
在这个例子中,onMounted
用于启动一个间隔,每秒向控制台记录一条消息。onBeforeUnmount
用于在组件卸载前清除该间隔。这可以防止组件不再使用后间隔继续运行。
与 Composables 集成
生命周期钩子可以在 Composables 内部有效使用,以管理它们封装的逻辑的生命周期。这使您能够创建可重用的逻辑,该逻辑可以自动处理设置和清理。
// useMouse.js
import { ref, onMounted, onUnmounted } from 'vue';
export function useMouse() {
const x = ref(0);
const y = ref(0);
function update(event) {
x.value = event.clientX;
y.value = event.clientY;
}
onMounted(() => {
window.addEventListener('mousemove', update);
});
onUnmounted(() => {
window.removeEventListener('mousemove', update);
});
return { x, y };
}
<template>
<div>
<p>Mouse position: x={{ x }}, y={{ y }}</p>
</div>
</template>
<script>
import { useMouse } from './useMouse';
export default {
setup() {
const { x, y } = useMouse();
return { x, y };
}
};
</script>
在这个例子中,useMouse
组合封装了跟踪鼠标位置的逻辑。onMounted
用于在组合首次使用时添加事件监听器,而 onUnmounted
用于在组合不再使用时移除事件监听器。
高级生命周期钩子使用
onErrorCaptured
onErrorCaptured
钩子允许你处理子组件中发生的错误。这可以用于记录错误、显示错误消息或防止应用程序崩溃。
<template>
<div>
<p>Parent Component</p>
<ChildComponent />
</div>
</template>
<script>
import { onErrorCaptured } from 'vue';
import ChildComponent from './ChildComponent.vue';
export default {
components: {
ChildComponent
},
setup() {
onErrorCaptured((err, instance, info) => {
console.error('Error captured:', err);
console.log('Component instance:', instance);
console.log('Error info:', info);
// Handle the error here
return false; // Prevents the error from propagating further
});
return {};
}
};
</script>
// ChildComponent.vue
<template>
<div>
<button @click="generateError">Generate Error</button>
</div>
</template>
<script>
export default {
setup() {
const generateError = () => {
throw new Error('This is a test error');
};
return {
generateError
};
}
};
</script>
在这个例子中,父组件中的 onErrorCaptured
钩子捕获了子组件抛出的错误。该钩子将错误记录到控制台,并阻止其进一步传播。
onRenderTracked
和 onRenderTriggered
这些钩子对于调试应用程序中的性能问题很有用。onRenderTracked
在组件的响应式依赖在渲染过程中被跟踪时被调用,而 onRenderTriggered
在响应式依赖触发重新渲染时被调用。
<template>
<div>
<p>{{ message }}</p>
<button @click="updateMessage">Update Message</button>
</div>
</template>
<script>
import { ref, onRenderTracked, onRenderTriggered } from 'vue';
export default {
setup() {
const message = ref('Initial Message');
onRenderTracked((event) => {
console.log('Dependency tracked:', event);
});
onRenderTriggered((event) => {
console.log('Re-render triggered by:', event);
});
const updateMessage = () => {
message.value = 'Updated Message';
};
return {
message,
updateMessage
};
}
};
</script>
在这个例子中,onRenderTracked
记录渲染过程中被追踪的依赖,而 onRenderTriggered
记录触发重新渲染的依赖。这可以帮助你识别不必要的重新渲染,并优化组件的性能。
onActivated
和 onDeactivated
这些钩子与 <keep-alive>
组件一起使用,用于管理缓存组件的生命周期。onActivated
在组件作为缓存子树的一部分插入到 DOM 中时被调用,而 onDeactivated
在组件作为缓存子树的一部分从 DOM 中移除时被调用。
<template>
<keep-alive>
<component :is="activeComponent" />
</keep-alive>
<button @click="toggleComponent">Toggle Component</button>
</template>
<script>
import { ref } from 'vue';
import ComponentA from './ComponentA.vue';
import ComponentB from './ComponentB.vue';
export default {
components: {
ComponentA,
ComponentB
},
setup() {
const activeComponent = ref('ComponentA');
const toggleComponent = () => {
activeComponent.value = activeComponent.value === 'ComponentA' ? 'ComponentB' : 'ComponentA';
};
return {
activeComponent,
toggleComponent
};
}
};
</script>
// ComponentA.vue
<template>
<div>
<p>Component A</p>
</div>
</template>
<script>
import { onActivated, onDeactivated } from 'vue';
export default {
setup() {
onActivated(() => {
console.log('Component A activated');
});
onDeactivated(() => {
console.log('Component A deactivated');
});
return {};
}
};
</script>
在这个例子中,<keep-alive>
组件缓存了当前组件。当切换当前组件时,会在被停用的组件上调用 onDeactivated
,在激活的组件上调用 onActivated
。