示例 1: 防抖 Hook(useDebounce
)
typescript
// hooks/useDebounce.ts
import { ref, watch, onUnmounted, type WatchSource } from 'vue';
/**
* 防抖 Hook
* @param source 监听的响应式数据源
* @param callback 防抖后执行的回调函数
* @param delay 防抖延迟时间(毫秒,默认 300ms)
*/
export function useDebounce<T>(
source: WatchSource<T>,
callback: (value: T) => void,
delay: number = 300
) {
let timeoutId: number | null = null;
// 监听数据源变化
watch(source, (newValue) => {
// 清除之前的定时器
if (timeoutId) {
clearTimeout(timeoutId);
}
// 设置新的定时器
timeoutId = setTimeout(() => {
callback(newValue);
}, delay);
});
// 组件卸载时清除定时器
onUnmounted(() => {
if (timeoutId) {
clearTimeout(timeoutId);
}
});
}
// 组件中使用示例:
// const searchQuery = ref('');
// useDebounce(searchQuery, (value) => {
// console.log('防抖后的搜索值:', value);
// });
示例 2: 本地存储 Hook(useLocalStorage
)
typescript
// hooks/useLocalStorage.ts
import { ref, watchEffect, type Ref } from 'vue';
/**
* 本地存储 Hook
* @param key 存储的键名
* @param defaultValue 默认值
* @returns 返回响应式变量和更新函数
*/
export function useLocalStorage<T>(
key: string,
defaultValue: T
): [Ref<T>, (value: T) => void] {
// 从 localStorage 读取初始值
const storedValue = localStorage.getItem(key);
const value = ref<T>(
storedValue ? JSON.parse(storedValue) : defaultValue
) as Ref<T>;
// 监听变化并保存到 localStorage
watchEffect(() => {
localStorage.setItem(key, JSON.stringify(value.value));
});
// 提供更新函数(可选,直接修改 value 也有效)
const updateValue = (newValue: T) => {
value.value = newValue;
};
return [value, updateValue];
}
// 组件中使用示例:
// const [theme, setTheme] = useLocalStorage<'light' | 'dark'>('theme', 'light');
示例 3: 网络请求 Hook(useFetch
)
typescript
// hooks/useFetch.ts
import { ref, type Ref } from 'vue';
interface UseFetchReturn<T> {
data: Ref<T | null>;
error: Ref<Error | null>;
loading: Ref<boolean>;
execute: () => Promise<void>;
}
/**
* 网络请求 Hook
* @param url 请求地址
* @param options 请求配置(可选)
*/
export function useFetch<T = unknown>(
url: string,
options?: RequestInit
): UseFetchReturn<T> {
const data = ref<T | null>(null);
const error = ref<Error | null>(null);
const loading = ref(false);
// 执行请求的函数
const execute = async () => {
loading.value = true;
try {
const response = await fetch(url, options);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
data.value = await response.json();
error.value = null;
} catch (err) {
error.value = err as Error;
data.value = null;
} finally {
loading.value = false;
}
};
return { data, error, loading, execute };
}
// 组件中使用示例:
// const { data, error, loading, execute } = useFetch<User[]>('/api/users');
// execute(); // 手动触发请求
示例 4: 倒计时 Hook(useCountdown
)
typescript
// hooks/useCountdown.ts
import { ref, onUnmounted } from 'vue';
/**
* 倒计时 Hook
* @param seconds 倒计时总秒数
* @returns 剩余时间(秒)和开始/重置函数
*/
export function useCountdown(seconds: number) {
const count = ref(seconds);
let timer: number | null = null;
// 开始/重置倒计时
const start = () => {
reset(); // 重置倒计时
timer = setInterval(() => {
if (count.value > 0) {
count.value--;
} else {
clearInterval(timer!);
}
}, 1000);
};
// 重置倒计时
const reset = () => {
if (timer) {
clearInterval(timer);
timer = null;
}
count.value = seconds;
};
// 组件卸载时清除定时器
onUnmounted(reset);
return { count, start, reset };
}
// 组件中使用示例:
// const { count, start } = useCountdown(60);
// start(); // 开始倒计时
示例 5: 事件监听 Hook(useEventListener
)
typescript
// hooks/useEventListener.ts
import { onMounted, onUnmounted } from 'vue';
type EventTarget = Window | Document | HTMLElement;
/**
* 事件监听 Hook
* @param target 目标元素(默认 window)
* @param event 事件名称
* @param listener 事件回调
*/
export function useEventListener(
event: string,
listener: EventListener,
target: EventTarget = window
) {
// 挂载时添加监听
onMounted(() => {
target.addEventListener(event, listener);
});
// 卸载时移除监听
onUnmounted(() => {
target.removeEventListener(event, listener);
});
}
// 组件中使用示例:
// useEventListener('click', (e) => {
// console.log('全局点击事件', e);
// });
最佳实践建议:
-
类型安全:
-
使用 TypeScript 接口 (
interface
) 明确函数参数和返回值类型。 -
用泛型 (
<T>
) 处理动态数据类型(如useFetch
中的响应数据)。
-
-
响应式处理:
-
优先使用
ref
替代reactive
(更适合类型推导)。 -
使用
watch
或watchEffect
处理副作用。
-
-
资源清理:
- 在
onUnmounted
中清除定时器、事件监听等资源。
- 在
-
组合复用:
- 多个简单 Hook 可以组合成复杂逻辑(如
useCountdown
+useEventListener
)。
- 多个简单 Hook 可以组合成复杂逻辑(如
-
推荐工具库:
- VueUse 提供了大量高质量的类型安全 Hooks。
通过这些示例,你可以逐步掌握如何编写类型安全、可复用的 Vue3 Hooks。实际开发中,根据需求灵活组合这些基础 Hooks,能显著提升代码质量和开发效率!