1. VueUse 是什么?为什么开发者都在用?
VueUse 是一个基于 Vue 3 Composition API 的工具函数集合,它提供了 200+ 个开箱即用的函数,覆盖了前端开发的方方面面。我第一次接触 VueUse 是在重构一个大型后台管理系统时,当时需要处理复杂的浏览器 API 交互和状态管理,手动实现这些功能既耗时又容易出错。
VueUse 的核心价值在于:
- 零依赖:只基于 Vue 3 的响应式系统
- 按需引入:每个函数都可以独立使用
- TypeScript 友好:完整的类型定义
- 跨平台支持:兼容 Vue 2/Vue 3、Nuxt 甚至纯 JavaScript
最让我惊喜的是它的设计理念——每个函数都是独立的组合式函数,这意味着你可以像搭积木一样组合它们。比如要实现一个拖拽上传功能,用 useDragAndDrop 配合 useFileSystemAccess 只需几行代码就能完成,而以前可能需要上百行实现。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 核心功能模块详解
2.1 状态管理类
useStorage 是我最常用的函数之一,它实现了 localStorage/sessionStorage 的响应式绑定:
javascript复制import { useStorage } from '@vueuse/core'
// 自动同步到 localStorage
const count = useStorage('my-count', 0)
进阶用法是自定义序列化方法:
javascript复制const user = useStorage('user', null, undefined, {
serializer: {
read: (v) => v ? JSON.parse(v) : null,
write: (v) => JSON.stringify(v)
}
})
2.2 DOM 操作类
useElementVisibility 可以轻松检测元素是否在视口中:
javascript复制const target = ref(null)
const isVisible = useElementVisibility(target)
watch(isVisible, (v) => {
console.log(v ? '进入视口' : '离开视口')
})
实际项目中我常用它配合 useIntersectionObserver 实现图片懒加载,性能比传统监听 scroll 事件提升 3 倍以上。
2.3 设备能力类
useClipboard 封装了剪贴板 API:
javascript复制const { copy, isSupported } = useClipboard()
const copyText = () => {
copy('要复制的文本').then(() => {
alert('复制成功!')
})
}
注意点:iOS 上需要用户主动触发(如点击事件),否则会静默失败。
2.4 实用工具类
useDebounceFn 解决频繁触发问题:
javascript复制const debouncedFn = useDebounceFn(() => {
console.log('防抖执行')
}, 500)
// 连续调用只会执行一次
debouncedFn()
debouncedFn()
对比传统实现,VueUse 版本会自动处理组件卸载时的清理工作。
3. 典型使用场景实战
3.1 后台管理系统
在权限控制模块,我这样组合多个函数:
javascript复制// 用户权限状态
const permissions = useStorage('admin-permissions', [])
// 实时检测屏幕尺寸
const { width } = useWindowSize()
// 响应式媒体查询
const isMobile = useMediaQuery('(max-width: 768px)')
// 权限校验函数
const hasPermission = (code) => {
return unref(permissions).includes(code)
}
3.2 数据可视化大屏
useRafFn 对动画性能提升明显:
javascript复制const { pause, resume } = useRafFn(() => {
chart.update() // 平滑更新图表
}, { immediate: true })
// 离开页面时自动暂停
useEventListener('visibilitychange', () => {
document.hidden ? pause() : resume()
})
3.3 移动端 H5 开发
手势操作组合方案:
javascript复制// 触摸反馈
const { isSupported: isTouchSupported } = useTouch()
// 长按触发
const { isLongPressed } = useLongPress(buttonRef)
// 滑动方向检测
const { direction } = useSwipe(touchAreaRef)
4. 完整项目集成示例
4.1 安装配置
推荐使用 pnpm 安装:
bash复制pnpm add @vueuse/core
Nuxt 3 需要额外配置:
javascript复制// nuxt.config.ts
export default defineNuxtConfig({
modules: [
'@vueuse/nuxt',
]
})
4.2 电商商品筛选案例
vue复制<script setup>
import { useStorage, useDebounceFn } from '@vueuse/core'
const searchKeyword = useStorage('search-keyword', '')
const filters = ref({
priceRange: [0, 1000],
categories: []
})
// 防抖搜索
const searchProducts = useDebounceFn(() => {
// 调用API...
}, 300)
// 监听筛选条件变化
watch([searchKeyword, filters], searchProducts)
</script>
<template>
<input v-model="searchKeyword" placeholder="搜索商品...">
<!-- 筛选组件... -->
</template>
4.3 性能优化技巧
- 按需引入避免打包体积过大:
javascript复制// 错误做法(导入整个库)
import { useMouse } from '@vueuse/core'
// 正确做法
import { useMouse } from '@vueuse/core/useMouse'
- SSR 兼容方案:
javascript复制// 服务端渲染时返回默认值
const mouseX = import.meta.server ? 0 : useMouse().x
- 自定义扩展函数:
javascript复制// extensions/useAuth.js
export function useAuth() {
const user = useStorage('user', null)
const isLogin = computed(() => !!user.value)
return { user, isLogin }
}
5. 常见问题排查
5.1 响应式失效问题
当在 setTimeout 中修改值时:
javascript复制const count = ref(0)
setTimeout(() => {
// 不会触发更新!
count.value++
}, 1000)
// 解决方案:使用 useTimeoutFn
const { start } = useTimeoutFn(() => {
count.value++ // 正常触发更新
}, 1000)
5.2 内存泄漏预防
自动清理的两种方式:
javascript复制// 方式1:使用可停止的函数
const { stop } = useEventListener('resize', handler)
onUnmounted(() => {
stop()
})
// 方式2:使用自动清理版本
useEventListener('resize', handler, { passive: true })
5.3 TypeScript 类型推断
自定义类型的最佳实践:
typescript复制interface User {
id: number
name: string
}
const user = useStorage<User>('user', { id: 0, name: '' })
// 现在 user.value 会有正确的类型提示
6. 进阶开发模式
6.1 函数组合技巧
实现带历史记录的状态管理:
javascript复制const state = ref(0)
const { undo, redo, canUndo, canRedo } = useRefHistory(state, {
capacity: 10 // 最多记录10步
})
// 操作示例
state.value = 1
state.value = 2
undo() // state.value → 1
6.2 与 Pinia 集成
在 store 中使用 VueUse:
javascript复制// stores/counter.js
import { defineStore } from 'pinia'
import { useLocalStorage } from '@vueuse/core'
export const useCounterStore = defineStore('counter', () => {
const count = useLocalStorage('pinia-count', 0)
return { count }
})
6.3 自定义函数开发
实现一个 useDarkMode:
javascript复制import { usePreferredDark, useStorage, useColorMode } from '@vueuse/core'
export function useDarkMode() {
const preferredDark = usePreferredDark()
const mode = useColorMode({
selector: 'body',
attribute: 'theme',
modes: {
dark: 'dark',
light: 'light'
}
})
const isDark = useStorage('dark-mode', preferredDark)
watch(isDark, (v) => {
mode.value = v ? 'dark' : 'light'
}, { immediate: true })
return { isDark }
}
在项目中使用 VueUse 一年多来,最大的体会是它让前端开发回归到"声明式"的本质。以前需要写大量样板代码的场景,现在往往几行组合式函数就能优雅解决。特别是在处理浏览器 API 和交互逻辑时,VueUse 提供的抽象层既保持了灵活性,又大幅降低了心智负担。
