vue3.x教程-自定义 Hooks 作者:马育民 • 2026-07-21 19:11 • 阅读:10000 # 介绍 **自定义 Hook 就是利用 Vue3 组合式 API(`ref/reactive/watch/onMounted` 等)封装逻辑的普通函数**。 > ⚠️ 区别:自定义 Hook ≠ 组件,没有模板,只封装**逻辑与状态** ### 作用 **抽离复用组件逻辑**,解决选项式 API 逻辑分散、难以复用的痛点 ### 命名规范 **以 `use` 开头**(约定俗成,便于识别),例如 `useMouse`、`useRequest` ### 使用 基于 ` {{ count }} + ``` # 响应式传递要点 ### 1. 返回 ref / reactive → 外部具备响应式(推荐) ```js export function useDemo() { const name = ref('张三') return { name } } ``` 组件中修改 `name.value` 会双向生效。 ### 2. 如果直接返回普通值 → 丢失响应式 ```js // ❌ 错误 return { name: name.value } ``` ### 3. 限制外部修改:封装只读状态 使用 `readonly` 保护内部状态,只开放方法修改 ```js import { ref, readonly } from 'vue' export function useCounter() { const count = ref(0) const add = () => count.value++ return { count: readonly(count), // 外部不能直接修改count.value add } } ``` # Hook 中使用生命周期、watch ### 示例:监听窗口大小 useWindowSize ```js import { ref, onMounted, onUnmounted, watch } from 'vue' export function useWindowSize() { const width = ref(window.innerWidth) const height = ref(window.innerHeight) const update = () => { width.value = window.innerWidth height.value = window.innerHeight } onMounted(() => { window.addEventListener('resize', update) }) onUnmounted(() => { window.removeEventListener('resize', update) }) return { width, height } } ``` > 关键优势:**自动生命周期销毁**,组件卸载自动清除事件,不用每个组件重复写解绑。 # 参数传递:支持传入响应式参数 Hook 可以接收 `ref` 响应式参数,内部可用 watch 监听 ```js import { ref, watch } from 'vue' export function useDouble(val) { const doubleVal = ref(val.value * 2) watch(val, (v) => { doubleVal.value = v * 2 }, { immediate: true }) return { doubleVal } } ``` 组件调用: ```js const num = ref(10) const { doubleVal } = useDouble(num) num.value = 20 // 自动触发hook内部watch更新 ``` # 组合多个 Hook(Hook 互相调用) Hook 可以引入其他 Hook,实现逻辑分层 ```js // useMouse.js export function useMouse() { const x = ref(0) const y = ref(0) // 监听鼠标移动... return { x, y } } // useMouseDistance.js import { useMouse } from './useMouse' import { computed } from 'vue' export function useMouseDistance() { const { x, y } = useMouse() const distance = computed(() => { return Math.sqrt(x.value ** 2 + y.value ** 2) }) return { distance, x, y } } ``` # 常见实战场景 Hook 模板 ### 1. useStorage 本地存储 ```js import { ref, watch } from 'vue' export function useStorage(key, defaultValue) { const stored = localStorage.getItem(key) const data = ref(stored ? JSON.parse(stored) : defaultValue) watch(data, (val) => { localStorage.setItem(key, JSON.stringify(val)) }, { deep: true }) return data } ``` ### 2. useRequest 简易请求封装 ```js import { ref } from 'vue' export function useRequest(apiFn) { const data = ref(null) const loading = ref(false) const error = ref(null) const run = async (...args) => { loading.value = true error.value = null try { data.value = await apiFn(...args) } catch (err) { error.value = err } finally { loading.value = false } } return { data, loading, error, run } } ``` # 常见坑与注意事项 1. **不要在条件/异步里调用 Hook** ```js // ❌ if (flag.value) { const { count } = useCounter() } ``` 2. **多个组件同时调用同一个 Hook,状态相互隔离** 每次调用 `useCounter()` 都会创建全新的 ref,组件之间互不干扰; 如果想要全局共享状态,要配合 `pinia` 或者单独导出一个外部 ref。 3. Hook 不是单例! ```js const a = useCounter() const b = useCounter() // a.count 和 b.count 相互独立 ``` 4. 全局共享逻辑方案 - 组件内复用逻辑 → **自定义Hook** - 跨组件共享状态 → **Pinia + Hook** 5. ` 原文出处:http://malaoshi.top/show_1GW3iZpCpZMv.html