Vue 3 的 Composition API 让逻辑复用变得直观,但很多常用功能依然需要开发者手动封装,比如防抖、节流、本地存储同步、鼠标位置追踪等。VueUse 是一个基于 Composition API 的工具库,提供了数百个高质量的组合式函数,覆盖浏览器 API、传感器、动画、状态管理等多个领域。它最大的价值在于把这些琐碎但高频的需求标准化,开发者只需要引入对应的函数,就能获得响应式数据和自动清理的副作用。

本文挑选三个最常用的组合式函数进行实战演示,它们分别解决输入防抖、本地存储持久化和鼠标跟踪问题。每个案例都会给出完整代码,并解释其内部原理和注意事项,帮助你快速掌握 VueUse 的使用方式。
一、使用 useDebounceFn 优化搜索框输入防抖
搜索框是典型的高频输入场景,如果用户每敲一个字符就发送一次请求,不仅浪费服务器资源,还可能因为响应顺序错乱导致结果错误。传统做法是在组件内手写一个 debounce 工具函数,用 setTimeout 和 clearTimeout 控制执行频率。这种方案虽然可行,但每次都要维护一个定时器变量,还要在组件卸载时手动清理,代码比较啰嗦。
VueUse 提供的 useDebounceFn 可以直接包装任意函数,返回一个防抖后的新函数。它内部使用 setTimeout 实现延迟执行,并在组件销毁时自动清除未触发的定时器,避免内存泄漏。下面是一个典型的搜索示例:
import { ref } from 'vue'
import { useDebounceFn } from '@vueuse/core'
export default {
setup() {
const keyword = ref('')
const results = ref([])
// 原始搜索函数,实际项目可替换为接口请求
async function doSearch(query) {
// 模拟请求延迟
await new Promise(resolve => setTimeout(resolve, 300))
results.value = [`${query} 的结果1`, `${query} 的结果2`]
}
// 创建 500ms 防抖后的搜索函数
const debouncedSearch = useDebounceFn(doSearch, 500)
function onInput(e) {
keyword.value = e.target.value
debouncedSearch(keyword.value)
}
return { keyword, results, onInput }
}
}
上面的代码中,useDebounceFn 接收两个参数:要执行的函数和延迟毫秒数。返回的 debouncedSearch 函数可以在用户停止输入 500 毫秒后才真正调用 doSearch。如果用户在 500 毫秒内继续输入,之前的定时器会被清除,重新计时。这样就保证了请求只在输入停顿时发出。
除了防抖,VueUse 还提供了 useThrottleFn 用于节流场景,比如滚动加载时限制事件触发频率。两者的核心差异在于:防抖是从最后一次触发开始计时,而节流是保证在固定时间间隔内最多执行一次。根据具体需求选择合适的函数即可。
需要注意的是,useDebounceFn 返回的函数不是响应式的,它只是一个带有防抖能力的普通函数。如果希望在模板中直接绑定防抖后的函数,可以配合 Vue 的事件修饰符或将其包装后在模板中调用,但通常在事件处理函数中手动调用会更加清晰。
二、使用 useLocalStorage 实现响应式本地存储
在 Web 应用中,把用户偏好设置、表单草稿或 Token 等数据保存到 localStorage 是常见需求。原生 localStorage 只支持字符串存取,存储对象时必须手动 JSON.stringify 和 JSON.parse,而且页面刷新后读取逻辑分散,维护成本高。更麻烦的是,当其他标签页修改了 localStorage 时,当前页面无法自动感知变化。
VueUse 的 useLocalStorage 返回一个响应式的 ref 对象,它的值会被自动序列化并写入 localStorage,同时也会监听 storage 事件,实现跨标签页同步。使用时只需传入存储键名和默认值,之后就像操作普通 ref 一样读写数据。
import { useLocalStorage } from '@vueuse/core'
export default {
setup() {
// 第一个参数是 localStorage 键名,第二个参数是默认值
const userSettings = useLocalStorage('app-settings', {
theme: 'light',
fontSize: 14,
notifications: true
})
function toggleTheme() {
userSettings.value.theme = userSettings.value.theme === 'light' ? 'dark' : 'light'
}
function increaseFont() {
userSettings.value.fontSize += 1
}
return { userSettings, toggleTheme, increaseFont }
}
}
在这个例子中,userSettings 是一个 Ref 对象,初始值会被序列化后存入 localStorage 的 app-settings 键下。之后任何对 userSettings.value 的修改都会自动触发写入。刷新页面后,useLocalStorage 会先尝试从 localStorage 读取并反序列化,如果键不存在才使用默认值。整个过程对开发者是透明的,不需要手动调用 localStorage.setItem 或 getItem。
使用 useLocalStorage 时需要注意序列化问题。默认情况下,它会使用 JSON 序列化,所以函数、Date 对象、Map 等特殊类型无法直接存储。如果确实需要存储复杂类型,可以传入自定义的 serializer 对象,定义 read 和 write 方法。另外,当存储的值是数组或对象时,使用 ref 的 .value 整体替换或修改内部属性都能触发同步,但如果直接修改数组的某个下标,响应式系统仍然可以追踪到,不过建议使用完整替换的方式以避免潜在问题。
另一个实用的特性是 useSessionStorage,它的用法与 useLocalStorage 完全一致,只是数据保存到 sessionStorage,关闭标签页后自动清除。这两个函数可以显著简化表单数据持久化的代码量。
三、使用 useMouse 和 useElementVisibility 增强交互体验
很多前端动效需要实时获取鼠标在页面中的坐标,例如自定义光标、视差背景或拖拽组件。原生实现通常要绑定 mousemove 事件,手动更新响应式变量,并在组件卸载时移除事件监听。这种样板代码充斥在各个组件中,不仅冗余还容易忘记清理。
VueUse 的 useMouse 组合式函数返回一个包含 x、y 坐标的 reactive 对象,并自动处理事件监听和清理。可以直接在模板中使用其属性,也可以通过计算属性进一步加工。下面展示一个简单的视差效果实现:
import { useMouse, computed } from '@vueuse/core'
export default {
setup() {
const { x, y } = useMouse()
// 将鼠标坐标映射到 -1 到 1 区间,用于控制位移比例
const offsetX = computed(() => (x.value / window.innerWidth - 0.5) * 20)
const offsetY = computed(() => (y.value / window.innerHeight - 0.5) * 20)
return { offsetX, offsetY }
}
}
模板中可以这样使用:
<template>
<div class="parallax" :style="{ transform: `translate(${offsetX}px, ${offsetY}px)` }">
跟随鼠标移动
</div>
</template>
useMouse 默认监听 window 对象的 mousemove 事件,也支持传入一个目标元素作为第一个参数,只追踪该元素内的鼠标坐标。比如 useMouse(containerRef) 会把坐标原点设为容器元素的左上角,这在处理拖拽或局部画布时非常有用。它返回的坐标已经经过 Vue 响应式处理,因此任何依赖坐标的计算属性都会自动更新。
除了鼠标跟踪,VueUse 还提供了 useElementVisibility,可以检测某个元素是否进入视口。这个函数基于 IntersectionObserver 实现,返回一个布尔值 ref。当元素可见时为 true,离开视口时为 false。配合 v-if 或 CSS 类可以实现懒加载、进入动画等功能。下面是一个简要示例:
import { ref, watch } from 'vue'
import { useElementVisibility } from '@vueuse/core'
export default {
setup() {
const target = ref(null)
const isVisible = useElementVisibility(target)
watch(isVisible, (visible) => {
if (visible) {
console.log('目标元素进入了视口')
}
})
return { target, isVisible }
}
}
这些组合式函数可以自由组合,比如用 useMouse 获取位置,配合 useElementVisibility 控制动画触发时机,就能构建出更细腻的交互效果。VueUse 的设计哲学是函数单一职责、返回响应式数据,因此多个函数之间很容易组合,这也正是 Composition API 的核心优势。
通过上述三个案例可以看到,VueUse 并不是一个庞大的框架,而是一组轻量、专注的实用函数。它解决了日常开发中高频出现的重复代码问题,并且因为 API 设计贴近原生,学习成本很低。建议在使用前先浏览官方文档,了解有哪些函数可以直接拿来用,避免重复造轮子。