模式切换
组合逻辑复用(自定义 Hook)
组合逻辑复用是 Vue 3 组合式 API 最强大的特性之一,它允许你将组件逻辑提取到可复用的函数中(称为 "自定义 Hook"),彻底解决了 Vue 2 中 mixins 带来的所有问题。
什么是自定义 Hook?
自定义 Hook 是一个使用 Vue 组合式 API 的函数,它可以:
- 封装和复用状态逻辑
- 包含响应式状态、计算属性、方法等
- 像普通函数一样被多个组件调用
- 实现比 mixins 更清晰、更安全的逻辑复用
基本结构
一个典型的自定义 Hook 包含以下部分:
javascript
// useFeature.js
import { ref, computed, onMounted } from 'vue'
export default function useFeature() {
// 1. 状态声明
const count = ref(0)
// 2. 计算属性
const doubleCount = computed(() => count.value * 2)
// 3. 方法
function increment() {
count.value++
}
// 4. 生命周期/副作用
onMounted(() => {
console.log('Hook mounted')
})
// 5. 返回暴露给组件的值
return {
count,
doubleCount,
increment
}
}
在组件中使用
vue
<script setup>
import useFeature from './useFeature'
const { count, doubleCount, increment } = useFeature()
</script>
<template>
<div>
<p>Count: {{ count }}</p>
<p>Double: {{ doubleCount }}</p>
<button @click="increment">+1</button>
</div>
</template>
核心优势
相比 Mixins 的优势
特性 | Mixins | 自定义 Hook |
---|---|---|
命名冲突 | 容易发生 | 无(明确命名空间) |
隐式依赖 | 难以追踪 | 显式导入 |
可组合性 | 有限 | 高度灵活 |
类型支持 | 困难 | 天然支持 TS |
代码组织 | 分散 | 逻辑集中 |
实际优势
- 明确的数据来源:知道每个状态/方法来自哪个 Hook
- 按需使用:可以选择性使用 Hook 的部分功能
- 嵌套组合:可以组合多个 Hook 创建更复杂的逻辑
进阶用法
接受参数
javascript
// useFetch.js
export default function useFetch(url) {
const data = ref(null)
const error = ref(null)
fetch(url)
.then(res => res.json())
.then(json => data.value = json)
.catch(err => error.value = err)
return { data, error }
}
组合多个 Hook
javascript
// useUserDashboard.js
export default function useUserDashboard(userId) {
const { user, loading } = useUser(userId)
const { posts } = useUserPosts(userId)
const { preferences } = useUserPreferences(userId)
return {
user,
posts,
preferences,
loading
}
}
副作用清理
javascript
// useEventListener.js
export default function useEventListener(target, event, callback) {
onMounted(() => target.addEventListener(event, callback))
onUnmounted(() => target.removeEventListener(event, callback))
}
最佳实践
命名约定
- 始终以
use
开头(如useFetch
,useMousePosition
) - 使用 camelCase 命名法
组织方式
src/
hooks/
useFetch.js
useLocalStorage.js
useMouse.js
性能优化
- 避免在 Hook 中创建不必要的响应式对象
- 对于不常变化的值,使用
shallowRef
或shallowReactive
- 使用
computed
缓存计算结果
TypeScript 支持
typescript
// useCounter.ts
interface UseCounterReturn {
count: Ref<number>
increment: () => void
reset: () => void
}
export default function useCounter(initialValue = 0): UseCounterReturn {
const count = ref(initialValue)
function increment() {
count.value++
}
function reset() {
count.value = initialValue
}
return {
count,
increment,
reset
}
}
实际案例
鼠标位置追踪
javascript
// useMousePosition.js
import { ref, onMounted, onUnmounted } from 'vue'
export default function useMousePosition() {
const x = ref(0)
const y = ref(0)
function update(e) {
x.value = e.pageX
y.value = e.pageY
}
onMounted(() => window.addEventListener('mousemove', update))
onUnmounted(() => window.removeEventListener('mousemove', update))
return { x, y }
}
本地存储同步
javascript
// useLocalStorage.js
import { ref, watch } from 'vue'
export default function useLocalStorage(key, initialValue) {
const stored = localStorage.getItem(key)
const data = ref(stored ? JSON.parse(stored) : initialValue)
watch(data, (newValue) => {
localStorage.setItem(key, JSON.stringify(newValue))
}, { deep: true })
return data
}
与 Vuex/Pinia 的关系
自定义 Hook:适合UI相关的局部状态逻辑
状态管理库:适合全局共享的状态
可以结合使用:
javascript// 在 Hook 中使用 Pinia import { useCartStore } from '@/stores/cart' export default function useCheckout() { const cart = useCartStore() function checkout() { // 使用 store 中的状态和方法 } return { checkout } }
总结
Vue 3 的自定义 Hook 系统提供了:
- 更清晰的逻辑复用 - 告别 mixins 的混乱
- 更好的代码组织 - 相关逻辑集中在一起
- 更强的类型支持 - 完美配合 TypeScript
- 更高的灵活性 - 可以组合和嵌套使用
通过合理使用自定义 Hook,你可以:
- 大幅减少重复代码
- 提高代码可维护性
- 创建高度可复用的逻辑单元
- 构建更健壮的 Vue 应用架构