在 Vue3 的 Composition API 中,自定义 Hook(Custom Hooks) 是提升代码复用性和可维护性的核心利器。它通过将组件逻辑封装为独立的函数,让开发者能够像"搭积木"一样快速构建复杂功能。本文将深入解析自定义 Hook 的设计原则,并推荐一系列开箱即用的 Vue3 Hook 库,助你高效开发!
一、为什么需要自定义 Hook?
在 Vue2 的 Options API 中,逻辑复用通常依赖 mixins
或高阶组件,但这些方案存在命名冲突 、隐式依赖 和来源不清晰 等问题。而 Vue3 的 Composition API 通过纯函数的方式解决了这些问题:
- 逻辑解耦:将相关逻辑(如数据获取、事件监听)封装到单个函数中。
- 命名清晰 :通过
useXXX
前缀明确标识 Hook,避免混淆。 - 灵活组合:像乐高一样拼接多个 Hook,构建复杂功能。
二、自定义 Hook 的核心设计原则
1. 单一职责原则
每个 Hook 应只关注一个功能。例如:
- ✅
useFetch
:仅处理数据请求。 - ❌
useUser
:同时处理数据请求、表单验证和权限控制。
2. 明确输入输出
通过参数和返回值定义清晰的接口:
javascript
// 示例:分页 Hook
interface PaginationConfig {
url: string;
pageSize: number;
}
export function usePagination(config: PaginationConfig) {
const [data, setData] = useState([]);
const [loading, setLoading] = useState(false);
const fetchPage = async (page: number) => {
setLoading(true);
const res = await fetch(`${config.url}?page=${page}`);
setData(await res.json());
setLoading(false);
};
return { data, loading, fetchPage };
}
3. 副作用管理
在 Hook 中添加事件监听、定时器等副作用时,必须通过 onMounted
和 onUnmounted
清理:
javascript
export function useWindowSize() {
const [width, setWidth] = useState(window.innerWidth);
useEffect(() => {
const handleResize = () => setWidth(window.innerWidth);
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
return width;
}
4. 响应式数据优化
- 基本类型用
ref
,对象/数组用reactive
。 - 避免不必要的响应式转换(如临时变量无需用
ref
)。
三、开箱即用的 Vue3 Hook 库推荐
1. VueUse
特点 :Vue 官方推荐的 Hook 库,提供 100+ 常用 Hook,覆盖动画、浏览器、状态管理、传感器等场景。
安装:
bash
npm install @vueuse/core
常用 Hook 示例:
-
useMouse
:追踪鼠标位置。javascriptimport { useMouse } from '@vueuse/core'; const { x, y } = useMouse();
-
useFetch
:简化数据请求。javascriptconst { data, isLoading } = useFetch('https://api.example.com/data');
-
useDebounceFn
:防抖函数。javascriptconst debouncedSearch = useDebounceFn((query) => { console.log('Search:', query); }, 500);
2. Composables
特点 :专注于 UI 逻辑的 Hook 集合,如表单验证、拖拽、虚拟滚动等。
安装:
bash
npm install @composables/core
示例:
-
useForm
:表单验证。javascriptconst { errors, validate } = useForm({ email: { required: true, email: true }, });
3. Ahooks-vue
特点 :受 React Ahooks 启发,提供状态管理、副作用、生命周期等 Hook。
安装:
bash
npm install ahooks-vue
示例:
-
useInterval
:定时器。javascriptuseInterval(() => { console.log('Tick every 1s'); }, 1000);
4. Vant Use
特点 :Vant UI 团队提供的移动端专用 Hook,如地理定位、设备信息等。
安装:
bash
npm install vant-use
示例:
-
useGeolocation
:获取用户位置。javascriptconst { position, error } = useGeolocation();
四、自定义 Hook 实战案例
案例 1:表格分页 Hook
javascript
export function useTablePagination<T>(apiUrl: string) {
const [data, setData] = useState<T[]>([]);
const [loading, setLoading] = useState(false);
const [pagination, setPagination] = useState({
page: 1,
pageSize: 10,
total: 0,
});
const fetchData = async () => {
setLoading(true);
const res = await fetch(`${apiUrl}?page=${pagination.page}&size=${pagination.pageSize}`);
const result = await res.json();
setData(result.list);
setPagination(prev => ({ ...prev, total: result.total }));
setLoading(false);
};
const handlePageChange = (page: number) => {
setPagination(prev => ({ ...prev, page }));
};
useEffect(() => {
fetchData();
}, [pagination.page, pagination.pageSize]);
return { data, loading, pagination, handlePageChange };
}
案例 2:主题切换 Hook
javascript
export function useTheme() {
const [theme, setTheme] = useState<'light' | 'dark'>('light');
useEffect(() => {
document.documentElement.className = theme;
}, [theme]);
const toggleTheme = () => {
setTheme(prev => (prev === 'light' ? 'dark' : 'light'));
};
return { theme, toggleTheme };
}
五、总结
- 自定义 Hook 是 Vue3 的核心优势,通过函数式编程提升代码复用性。
- 遵循设计原则:单一职责、明确接口、副作用管理、响应式优化。
- 善用开源库:VueUse、Composables 等库覆盖了大多数场景,避免重复造轮子。
- 从简单到复杂:先封装基础 Hook(如数据请求),再逐步构建高级组合。
六、资源推荐
- 官方文档 :vueuse.org(按分类浏览所有 Hook)
- 示例仓库 :VueUse Playground
- 进阶技巧 :结合
unplugin-vue-components
实现自动按需引入