Vue3 Hooks:从原理到实战封装指南
一、Hooks 的定义与核心价值
在 Vue3 的 Composition API 体系中,Hooks(组合式函数) 是通过封装响应式逻辑来实现代码复用的核心方案。其核心思想借鉴 React Hooks,但结合 Vue 的响应式系统形成了独特的实现方式。
与传统方案的对比:
方案 | 优点 | 缺点 |
---|---|---|
Mixins | 逻辑复用 | 命名冲突、来源不明确、难以追踪 |
工具函数 | 纯函数无副作用 | 无法使用响应式特性 |
Hooks | 响应式支持、逻辑组合、作用域隔离 | 需要理解响应式原理 |
二、Hooks 与普通函数的本质区别
- 响应式能力
// Hooks 内部使用响应式 API
import { ref, onMounted } from 'vue'
export function useCounter(initialValue = 0) {
const count = ref(initialValue)
const increment = () => count.value++
return { count, increment }
}
- 生命周期集成
function useMouse() {
const x = ref(0)
const y = ref(0)
const update = e => {
x.value = e.pageX
y.value = e.pageY
}
onMounted(() => window.addEventListener('mousemove', update))
onUnmounted(() => window.removeEventListener('mousemove', update))
return { x, y }
}
- 上下文感知
// 普通函数无法访问组件上下文
function commonFn() {
// 无法访问 this.$route 等实例属性
}
// Hooks 可通过组合式 API 获取上下文
import { getCurrentInstance } from 'vue'
function useRouter() {
const { proxy } = getCurrentInstance()
return proxy.$router
}
三、高质量 Hooks 封装原则
- 单一职责模式
// Bad: 混杂多种功能
function useUser() {
// 用户数据、权限、配置混杂
}
// Good: 拆分独立 Hooks
function useUserProfile() {...}
function useUserPermissions() {...}
- 灵活配置设计
function usePagination(api, options = {}) {
const {
pageSize = 10,
immediate = true,
formatter = data => data
} = options
// ...
}
- 副作用管理
function useEventListener(target, event, callback) {
onMounted(() => target.addEventListener(event, callback))
onUnmounted(() => target.removeEventListener(event, callback))
}
- TypeScript 强化
interface DarkModeOptions {
storageKey?: string
defaultState?: boolean
}
export function useDarkMode(
options: DarkModeOptions = {}
): { isDark: Ref<boolean>; toggle: () => void } {
// ...
}
四、企业级常用 Hooks 实现
- 智能请求 Hook
export function useRequest(api, config = {}) {
const loading = ref(false)
const data = ref(null)
const error = ref(null)
const execute = async params => {
try {
loading.value = true
const res = await api(params)
data.value = config.format?.(res) || res
} catch (err) {
error.value = err
} finally {
loading.value = false
}
}
return { loading, data, error, execute }
}
- 本地存储同步 Hook
export function useLocalStorage(key, defaultValue) {
const state = ref(defaultValue)
const read = () => {
const value = localStorage.getItem(key)
if (value !== null) state.value = JSON.parse(value)
}
const write = () => {
localStorage.setItem(key, JSON.stringify(state.value))
}
read() // 初始化读取
watch(state, write, { deep: true })
return state
}
- 响应式视口尺寸跟踪
export function useViewport() {
const width = ref(window.innerWidth)
const height = ref(window.innerHeight)
const update = () => {
width.value = window.innerWidth
height.value = window.innerHeight
}
useEventListener(window, 'resize', update)
return { width, height }
}
- 智能滚动 Hook
export function useScroll(refEl) {
const x = ref(0)
const y = ref(0)
const isBottom = ref(false)
const el = refEl || window
const handler = () => {
if (el === window) {
x.value = window.scrollX
y.value = window.scrollY
isBottom.value =
window.innerHeight + window.scrollY >= document.body.offsetHeight
} else {
x.value = el.value.scrollLeft
y.value = el.value.scrollTop
isBottom.value =
el.value.scrollHeight <= el.value.clientHeight + el.value.scrollTop
}
}
useEventListener(el, 'scroll', handler)
handler() // 初始触发
return { x, y, isBottom }
}
五、高级技巧与最佳实践
- Hooks 组合
function useUserDashboard() {
const { user } = useAuth()
const { data: projects } = useProjectList(user.value.id)
const { data: tasks } = useTaskList(user.value.id)
return { user, projects, tasks }
}
- 性能优化
function useHeavyCalculation(data) {
const result = computed(() => {
// 复杂计算使用 computed 缓存
return heavyOperation(data.value)
})
return result
}
- SSR 兼容
import { onServerPrefetch } from 'vue'
function useSSRData() {
const data = ref(null)
const fetchData = async () => {
data.value = await fetch('/api/data')
}
onServerPrefetch(fetchData)
onMounted(!data.value && fetchData)
return data
}
六、总结与建议
何时使用 Hooks:
- 需要跨组件复用的逻辑
- 复杂组件的逻辑拆分
- 需要响应式状态管理的工具函数
通过合理使用 Hooks,开发者可以构建出高内聚、低耦合的前端应用架构。建议在项目中建立 src/hooks
目录进行分类管理,结合 TypeScript 和单元测试构建企业级 Hook 库。