掌握Composition API核心技巧,提升代码复用性与可维护性
为什么需要自定义Hook?
在Vue3中,Composition API彻底改变了我们组织组件逻辑的方式。而自定义Hook(也称为组合函数)是Composition API最强大的功能之一,它允许我们将可复用的逻辑提取到独立的函数中,从而解决以下问题:
- 在多个组件中重复相同逻辑
- 大型组件中逻辑难以追踪
- 业务逻辑与组件生命周期耦合过紧
- 跨组件状态共享困难
实战案例:构建网络请求自定义Hook
让我们通过创建一个网络请求Hook来演示自定义Hook的强大功能。这个Hook将处理以下功能:
- 发起HTTP请求
- 管理加载状态
- 处理错误
- 缓存响应数据
步骤1:创建useFetch Hook
import { ref } from 'vue';
export function useFetch(url) {
const data = ref(null);
const error = ref(null);
const loading = ref(false);
const fetchData = async () => {
loading.value = true;
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
data.value = await response.json();
error.value = null;
} catch (err) {
error.value = err.message || '请求失败';
data.value = null;
} finally {
loading.value = false;
}
};
// 立即执行请求
fetchData();
return { data, error, loading, refetch: fetchData };
}
步骤2:在组件中使用Hook
<template>
<div>
<div v-if="loading">加载中...</div>
<div v-else-if="error" class="error">{{ error }}</div>
<div v-else>
<h2>用户数据</h2>
<ul>
<li v-for="user in data" :key="user.id">
{{ user.name }} - {{ user.email }}
</li>
</ul>
</div>
<button @click="refetch">重新加载</button>
</div>
</template>
<script setup>
import { useFetch } from '@/hooks/useFetch';
const { data, error, loading, refetch } = useFetch('https://api.example.com/users');
</script>
进阶技巧:带参数的自定义Hook
让我们创建一个更高级的计数器Hook,它不仅可以计数,还可以记录计数历史:
import { ref, computed } from 'vue';
export function useCounter(initialValue = 0) {
const count = ref(initialValue);
const history = ref([initialValue]);
const increment = (amount = 1) => {
count.value += amount;
history.value.push(count.value);
};
const decrement = (amount = 1) => {
count.value -= amount;
history.value.push(count.value);
};
const reset = () => {
count.value = initialValue;
history.value.push(count.value);
};
const historyCount = computed(() => history.value.length);
return {
count,
increment,
decrement,
reset,
history,
historyCount
};
}
在组件中使用计数器Hook
当前计数: {{ count }}
历史记录数: {{ historyCount }}
自定义Hook的最佳实践
命名规范
始终以”use”开头命名Hook,例如useFetch、useCounter,这有助于区分普通函数和组合函数。
单一职责
每个Hook应该只关注解决一个特定问题,保持功能集中和简洁。
响应式返回
返回响应式对象或ref,确保在组件中使用时能够触发更新。
灵活的参数
通过参数配置使Hook更灵活,但要避免过多参数导致复杂度过高。
// 演示计数器的简单实现
document.addEventListener(‘DOMContentLoaded’, () => {
const demoElement = document.querySelector(‘.counter-demo’);
if (demoElement) {
let count = 0;
let history = [0];
const updateDisplay = () => {
demoElement.innerHTML = `
当前计数: ${count}
历史记录数: ${history.length}
`;
// 添加事件监听
demoElement.querySelector(‘.inc’).addEventListener(‘click’, () => {
count++;
history.push(count);
updateDisplay();
});
demoElement.querySelector(‘.dec’).addEventListener(‘click’, () => {
count–;
history.push(count);
updateDisplay();
});
demoElement.querySelector(‘.inc5’).addEventListener(‘click’, () => {
count += 5;
history.push(count);
updateDisplay();
});
demoElement.querySelector(‘.reset’).addEventListener(‘click’, () => {
count = 0;
history.push(count);
updateDisplay();
});
};
updateDisplay();
}
});