VueUse 组合式工具集
VueUse 组合式工具集:从入门到工程化实践
什么是 VueUse?
VueUse 是基于 Vue 3 Composition API 的实用工具函数集合,由开发者 Anthony Fu 创建并维护。它封装了数百个与浏览器 API、状态管理、传感器、动画、时间等相关的组合式函数(Composables),帮助你在 Vue 项目中以声明式、响应式的方式使用原生能力,显著减少样板代码。
核心理念:
- Composition First:所有功能都以
use...的形式提供,完美融入setup或<script setup>语法。 - 按需引入与 Tree‑shaking:只打包你实际用到的函数,零负担接入。
- 跨平台支持:多数工具同时兼容浏览器、Node.js、Nuxt 等环境(部分通过
@vueuse/integrations提供扩展)。 - 类型安全:完整的 TypeScript 支持,类型推导开箱即用。
快速上手
安装
在现有 Vue 3 项目中添加 VueUse:
npm install @vueuse/core
或使用 yarn / pnpm:
yarn add @vueuse/core
pnpm add @vueuse/core
第一个组合式函数:useMouse
useMouse 用于跟踪鼠标在页面中的坐标,它是学习 VueUse 的最佳起点。
<script setup>
import { useMouse } from '@vueuse/core'
const { x, y } = useMouse()
</script>
<template>
<div>鼠标坐标:X {{ x }}, Y {{ y }}</div>
</template>
x和y是响应式引用,会在鼠标移动时自动更新。- 组件卸载后监听器会自动清理,无需手动
removeEventListener。
核心功能分类与典型用法
VueUse 提供了超过 200 个函数,官方文档将其按功能划分为多个类别。以下挑选高频工具进行介绍。
浏览器与传感器
这些函数将浏览器原生 API 转化为响应式数据。
useWindowSize:监听窗口尺寸变化。
<script setup>
import { useWindowSize } from '@vueuse/core'
const { width, height } = useWindowSize()
</script>
<template>
<p>窗口宽度:{{ width }}px,高度:{{ height }}px</p>
</template>
useGeolocation:获取地理位置。
import { useGeolocation } from '@vueuse/core'
const { coords, locatedAt, error } = useGeolocation()
usePreferredDark:检测系统是否偏好深色主题。
<script setup>
import { usePreferredDark } from '@vueuse/core'
const isDark = usePreferredDark()
</script>
<template>
<div :class="{ dark: isDark }">自动适应深色模式</div>
</template>
useOnline:检测网络连接状态。
import { useOnline } from '@vueuse/core'
const online = useOnline()
状态与存储
useLocalStorage / useSessionStorage:将响应式数据自动同步到 Web Storage。
import { useLocalStorage } from '@vueuse/core'
// 创建一个与 localStorage 中 `user-settings` 键绑定的 ref
const settings = useLocalStorage('user-settings', { theme: 'auto', fontSize: 14 })
// 修改 settings.value 会自动写入 localStorage
- 支持对象序列化,可传入自定义
serializer。 - 多标签页同步(监听
storage事件)。
useStorage:通用存储接口,可适配 sessionStorage、localStorage 或自定义存储。
import { useStorage } from '@vueuse/core'
const token = useStorage('auth-token', null, sessionStorage)
useRefHistory:为任何 ref 添加撤销/重做历史。
import { ref } from 'vue'
import { useRefHistory } from '@vueuse/core'
const text = ref('')
const { undo, redo, canUndo, canRedo, history } = useRefHistory(text)
非常适合文本编辑器、表单草稿等场景。
DOM 与工具函数
onClickOutside:点击元素外部触发回调。
<script setup>
import { ref } from 'vue'
import { onClickOutside } from '@vueuse/core'
const target = ref(null)
onClickOutside(target, () => { console.log('点击了外部') })
</script>
<template>
<div ref="target">点击这个 div 的外部试试</div>
</template>
useIntersectionObserver:元素可见性侦测,用于懒加载或无限滚动。
<script setup>
import { ref } from 'vue'
import { useIntersectionObserver } from '@vueuse/core'
const el = ref(null)
const isVisible = ref(false)
useIntersectionObserver(el, ([{ isIntersecting }]) => {
isVisible.value = isIntersecting
})
</script>
useVModel:简化 v-model 在自定义组件中的双向绑定。
<!-- 子组件 -->
<script setup>
import { useVModel } from '@vueuse/core'
const props = defineProps({ modelValue: String })
const emit = defineEmits(['update:modelValue'])
const value = useVModel(props, 'modelValue', emit)
</script>
<template>
<input v-model="value" />
</template>
useClipboard:复制文本到剪贴板。
import { useClipboard } from '@vueuse/core'
const { text, copy, copied, isSupported } = useClipboard()
copy('Hello VueUse') // 复制完成后 copied 变为 true
网络请求与异步控制
虽非 axios 替代品,但 VueUse 提供了轻量级的请求管理工具。
useFetch:对 fetch 的响应式封装。
<script setup>
import { useFetch } from '@vueuse/core'
const { data, isFetching, error, execute } = useFetch('https://api.example.com/data').json()
</script>
- 自动处理请求状态、取消操作。
- 支持 ref 驱动的 URL,当 URL 变化时自动重新请求。
useAxios(需安装 @vueuse/integrations 和 axios):
import { useAxios } from '@vueuse/integrations/useAxios'
const { data, isLoading } = useAxios('/api/users')
工具与时间
useDebounceFn / useThrottleFn:防抖与节流。
import { useDebounceFn } from '@vueuse/core'
const debouncedSearch = useDebounceFn((query) => {
// 请求接口
}, 500)
也可以直接使用 refDebounced 来控制 ref 的防抖:
import { refDebounced } from '@vueuse/core'
const input = ref('')
const debounced = refDebounced(input, 300)
watch(debounced, (val) => { /* 搜索逻辑 */ })
useNow:实时更新的当前时间。
<script setup>
import { useNow } from '@vueuse/core'
const now = useNow()
</script>
<template>
<div>{{ now.toLocaleTimeString() }}</div>
</template>
useIntervalFn:可控制的 setInterval。
import { useIntervalFn } from '@vueuse/core'
const { pause, resume, isActive } = useIntervalFn(() => {
console.log('每秒执行一次')
}, 1000)
工程化配置与注意事项
-
按需引入与打包体积 VueUse 支持 Tree‑shaking,只需从
@vueuse/core导入具体函数,未被使用的代码不会打包。import { useMouse, useLocalStorage } from '@vueuse/core'不建议
import * as VueUse from ...,这会引入整个库。 -
SSR(服务端渲染)兼容性 VueUse 会自动判断运行环境,若代码在服务端执行,部分浏览器 API 会被安全跳过或返回默认值。你可以在
nuxt.config.ts中直接使用@vueuse/nuxt模块获得更好的 SSR 支持(自动导入等)。 -
与 Nuxt 集成 对于 Nuxt 3 项目,推荐安装
@vueuse/nuxt模块。// nuxt.config.ts export default defineNuxtConfig({ modules: ['@vueuse/nuxt'], })此后所有核心 composables 将自动导入,无需手动 import。
-
自定义配置 某些函数支持全局配置。例如
useStorage可设置默认序列化器:import { configure } from '@vueuse/core' configure({ storage: { serializer: { read: ..., write: ... } } })
实战案例:打造一个待办事项应用
整合多个 VueUse 工具,快速构建功能完整的小应用。
需求:
- 添加/删除/编辑待办
- 数据持久化至 localStorage
- 撤销/重做操作
- 实时显示当前时间
- 点击外部关闭编辑
<script setup>
import { ref } from 'vue'
import { useLocalStorage, useRefHistory, useNow, onClickOutside } from '@vueuse/core'
// 持久化待办列表
const todos = useLocalStorage('vueuse-todos', [])
const { undo, redo, canUndo, canRedo } = useRefHistory(todos, { deep: true })
const newTodo = ref('')
const editingIndex = ref(-1)
const editInput = ref(null)
const now = useNow()
function addTodo() {
if (!newTodo.value.trim()) return
todos.value.push({ text: newTodo.value, done: false })
newTodo.value = ''
}
function removeTodo(index) {
todos.value.splice(index, 1)
}
function startEdit(index) {
editingIndex.value = index
// 下一帧自动聚焦编辑输入
nextTick(() => editInput.value?.focus())
}
// 点击编辑框外部时退出编辑状态
onClickOutside(editInput, () => { editingIndex.value = -1 })
</script>
<template>
<div>
<h2>📝 待办事项</h2>
<p>当前时间:{{ now.toLocaleString() }}</p>
<div>
<input v-model="newTodo" @keyup.enter="addTodo" placeholder="添加新任务" />
<button @click="addTodo">添加</button>
</div>
<ul>
<li v-for="(todo, index) in todos" :key="index">
<input type="checkbox" v-model="todo.done" />
<span v-if="editingIndex !== index">{{ todo.text }}</span>
<input v-else ref="editInput" v-model="todo.text" @keyup.enter="editingIndex = -1" />
<button @click="startEdit(index)">编辑</button>
<button @click="removeTodo(index)">删除</button>
</li>
</ul>
<div>
<button :disabled="!canUndo" @click="undo">↩ 撤销</button>
<button :disabled="!canRedo" @click="redo">↪ 重做</button>
</div>
</div>
</template>
扩展资源与最佳实践
- 官方文档:vueuse.org 提供了完整的函数列表、在线演示和 TypeScript 声明。
- VueUse Playground:在文档中可以直接修改示例代码并实时查看效果。
- @vueuse/components:提供无渲染组件(Renderless Components),如
<UseMouse>、<UseDark>等,适用于偏好模板语法的开发者。 - 最佳实践:
- 将 VueUse 函数视为原子逻辑块,继续在自定义 composables 中组合它们,形成项目专属的业务逻辑层。
- 利用
effectScope和onScopeDispose管理副作用清理(VueUse 内部已处理多数清理)。 - 关注
@vueuse/integrations提供的useAxios、useJwt、useFocusTrap等与第三方库桥接的函数,避免重复造轮子。
VueUse 不仅是工具集,更是一种“Composition API 思维”的具体展现。熟练运用它将大幅提升你的 Vue 开发效率,让代码更简洁、更健壮。