VueUse 常用 Hooks 使用指南
VueUse 是一个基于 Vue Composition API 的实用工具库,提供了大量开箱即用的功能。本文将介绍 15 个最常用的 VueUse Hooks,帮助您提升开发效率。
下面是 VueUse 常用 hooks 的使用场景总结,帮助您在不同开发需求中快速选择合适的工具:
🕒 时间处理
useNow + useDateFormat
- 使用场景:实时时钟显示、倒计时组件、需要精确时间戳的日志系统
- 优势:自动更新时间,无需手动处理定时器
🔄 双向数据绑定
useVModel
- 使用场景:自定义表单组件、复杂输入控件、需要简化 v-model 逻辑的场景
- 优势:减少父子组件通信的样板代码
🌐 URL 操作
useUrlSearchParams
- 使用场景:
- 分页组件
- 筛选和排序功能
- 分享链接状态保持
- 优势:自动同步 URL 参数与组件状态
📋 剪贴板操作
useClipboard
- 使用场景:
- "复制链接"按钮
- 代码片段分享
- 数据导出功能
- 优势:提供复制状态反馈,简化 Clipboard API 使用
📏 元素尺寸与位置
useElementBounding
- 使用场景:
- 拖拽组件
- 定位浮层/工具提示
- 滚动动画效果
useElementSize
- 使用场景:
- 响应式图表容器
- 自适应布局组件
- 元素尺寸变化监听
👀 可视区域检测
useElementVisibility
- 使用场景:
- 图片/内容懒加载
- 滚动到视图时的动画触发
- 用户阅读进度跟踪
⏱️ 性能优化
useDebounceFn
- 使用场景:
- 搜索框输入
- 窗口大小调整事件
- 高频触发的事件处理
- 优势:减少不必要的计算和请求
🧬 DOM 变化监听
useMutationObserver
- 使用场景:
- 第三方组件集成
- 富文本编辑器
- 动态内容注入监控
📌 全局状态管理
createGlobalState
- 使用场景:
- 用户偏好设置(主题、语言)
- 简单的全局计数器
- 跨组件共享的小型状态
createInjectionState
- 使用场景:
- 组件库开发
- 复杂表单状态管理
- 深层嵌套组件通信
🖱️ UI 交互
vOnClickOutside
- 使用场景:
- 下拉菜单关闭
- 模态框关闭
- 弹出层隐藏
🔄 状态切换
useToggle
- 使用场景:
- 开关组件
- 显示/隐藏切换
- 多状态切换控制
- 优势:简化布尔值状态管理
📐 响应式布局
useResizeObserver
- 使用场景:
- 自适应组件
- 画布尺寸调整
- 响应式图表重绘
状态持久化
useLocalStorage+ useSessionStorage
使用场景:
- 用户偏好设置(主题、字体大小)持久化
- 表单草稿自动保存
- 跨页面状态共享(如购物车)
使用场景对照表
| 需求场景 | 推荐 Hook |
|---|---|
| 实时显示时间 | useNow + useDateFormat |
| 自定义表单组件 | useVModel |
| URL 参数管理 | useUrlSearchParams |
| 复制到剪贴板功能 | useClipboard |
| 元素位置信息获取 | useElementBounding |
| 懒加载实现 | useElementVisibility |
| 搜索框防抖 | useDebounceFn |
| 监听DOM变化 | useMutationObserver |
| 全局主题切换 | createGlobalState |
| 复杂表单状态共享 | createInjectionState |
| 点击外部关闭弹窗 | vOnClickOutside |
| 开关状态管理 | useToggle |
| 响应式布局实现 | useResizeObserver |
| 元素尺寸获取 | useElementSize |
📍 1. useNow - 实时获取当前时间
<script setup>
import { useNow, useDateFormat } from '@vueuse/core'
// 获取当前时间响应式对象
const now = useNow()
// 格式化时间
const formattedTime = useDateFormat(now, 'YYYY-MM-DD HH:mm:ss')
</script>
<template>
<div class="card">
<h3>useNow & useDateFormat</h3>
<p>当前时间: {{ formattedTime }}</p>
</div>
</template>
2. useVModel - 简化 v-model 绑定
<script setup>
import { useVModel } from '@vueuse/core'
const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])
// 创建双向绑定的 ref
const value = useVModel(props, 'modelValue', emit)
// 示例:在输入框中使用
const handleInput = (e) => value.value = e.target.value
</script>
<template>
<div class="card">
<h3>useVModel</h3>
<input :value="value" @input="handleInput" placeholder="输入内容...">
<p>绑定值: {{ value }}</p>
</div>
</template>
👀 3. useElementVisibility - 检测元素可见性
<script setup>
import { ref } from 'vue'
import { useElementVisibility } from '@vueuse/core'
const el = ref(null)
const isVisible = useElementVisibility(el)
</script>
<template>
<div class="card">
<h3>useElementVisibility</h3>
<div ref="el" class="visibility-box">
{{ isVisible ? '元素在视窗中' : '元素不在视窗中' }}
</div>
<div class="spacer"></div>
</div>
</template>
<style>
.visibility-box {
height: 150px;
background: var(--color-primary);
display: flex;
align-items: center;
justify-content: center;
color: white;
border-radius: 8px;
}
.spacer {
height: 200vh;
}
</style>
🔍 4. useUrlSearchParams - 操作 URL 查询参数
<script setup>
import { useUrlSearchParams } from '@vueuse/core'
// 使用 history 模式
const params = useUrlSearchParams('history')
// 示例:设置搜索参数
const setSearchParam = () => {
params.search = 'VueUse'
}
</script>
<template>
<div class="card">
<h3>useUrlSearchParams</h3>
<p>当前查询参数: {{ JSON.stringify(params) }}</p>
<button @click="setSearchParam">设置 search=VueUse</button>
<button @click="params.page = 1">设置 page=1</button>
</div>
</template>
🌐 5. createGlobalState - 创建全局状态
<script setup>
import { createGlobalState, useStorage } from '@vueuse/core'
// 创建全局状态
const useGlobalCounter = createGlobalState(
() => useStorage('global-counter', 0)
)
const counter = useGlobalCounter()
</script>
<template>
<div class="card">
<h3>createGlobalState</h3>
<p>全局计数器: {{ counter }}</p>
<button @click="counter++">增加</button>
<button @click="counter--">减少</button>
</div>
</template>
📋 6. useClipboard - 剪贴板操作
<script setup>
import { ref } from 'vue'
import { useClipboard } from '@vueuse/core'
const source = ref('复制这段文本')
const { copy, copied } = useClipboard()
const handleCopy = () => {
copy(source.value)
}
</script>
<template>
<div class="card">
<h3>useClipboard</h3>
<p>文本内容: {{ source }}</p>
<button @click="handleCopy">
{{ copied ? '已复制!' : '复制到剪贴板' }}
</button>
</div>
</template>
📏 7. useElementBounding - 获取元素边界信息
<script setup>
import { ref } from 'vue'
import { useElementBounding } from '@vueuse/core'
const el = ref(null)
const rect = useElementBounding(el)
// 响应窗口大小变化
const { width, height, top, left, bottom, right } = rect
</script>
<template>
<div class="card">
<h3>useElementBounding</h3>
<div ref="el" class="bounding-box">
调整窗口大小查看变化
</div>
<div class="bounding-info">
<p>宽度: {{ width.toFixed(0) }}px</p>
<p>高度: {{ height.toFixed(0) }}px</p>
<p>顶部: {{ top.toFixed(0) }}px</p>
<p>左侧: {{ left.toFixed(0) }}px</p>
</div>
</div>
</template>
<style>
.bounding-box {
width: 100%;
height: 120px;
background: var(--color-primary);
color: white;
display: flex;
align-items: center;
justify-content: center;
border-radius: 8px;
margin-bottom: 16px;
}
.bounding-info {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 8px;
}
</style>
🖱️ 8. vOnClickOutside - 点击外部事件
<script setup>
import { vOnClickOutside } from '@vueuse/components'
import { ref } from 'vue'
const showModal = ref(false)
const modal = ref(null)
const openModal = () => showModal.value = true
const closeModal = () => showModal.value = false
</script>
<template>
<div class="card">
<h3>vOnClickOutside</h3>
<button @click="openModal">打开弹窗</button>
<div v-if="showModal" ref="modal" v-on-click-outside="closeModal" class="modal">
<p>点击弹窗外部区域关闭</p>
<button @click="closeModal">关闭弹窗</button>
</div>
</div>
</template>
<style>
.modal {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: white;
padding: 24px;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
z-index: 100;
}
</style>
⏳ 9. useDebounceFn - 防抖函数
<script setup>
import { ref } from 'vue'
import { useDebounceFn } from '@vueuse/core'
const searchQuery = ref('')
const results = ref([])
// 创建防抖函数
const debouncedSearch = useDebounceFn(() => {
results.value = [`结果: ${searchQuery.value}`, `相关: ${searchQuery.value}-1`, `相关: ${searchQuery.value}-2`]
}, 500)
// 监听输入变化
watch(searchQuery, debouncedSearch)
</script>
<template>
<div class="card">
<h3>useDebounceFn</h3>
<input v-model="searchQuery" placeholder="输入搜索内容..." />
<ul v-if="results.length">
<li v-for="(result, index) in results" :key="index">{{ result }}</li>
</ul>
<p v-else>输入内容开始搜索 (500ms防抖)</p>
</div>
</template>
📐 10. useElementSize - 获取元素尺寸
<script setup>
import { ref } from 'vue'
import { useElementSize } from '@vueuse/core'
const el = ref(null)
const { width, height } = useElementSize(el)
</script>
<template>
<div class="card">
<h3>useElementSize</h3>
<div ref="el" class="resizable-box">
<p>调整此元素大小</p>
<p>宽度: {{ width }}px</p>
<p>高度: {{ height }}px</p>
</div>
</div>
</template>
<style>
.resizable-box {
resize: both;
overflow: auto;
background: var(--color-primary);
color: white;
padding: 16px;
border-radius: 8px;
min-height: 100px;
min-width: 150px;
}
</style>
🧬 11. useMutationObserver - DOM 变化观察
<script setup>
import { ref } from 'vue'
import { useMutationObserver } from '@vueuse/core'
const el = ref(null)
const messages = ref([])
useMutationObserver(
el,
(mutations) => {
mutations.forEach(mutation => {
messages.value.push(`DOM 变化类型: ${mutation.type}`)
})
},
{ childList: true, subtree: true }
)
const addElement = () => {
const newEl = document.createElement('div')
newEl.textContent = '新元素 ' + (el.value.children.length + 1)
el.value.appendChild(newEl)
}
</script>
<template>
<div class="card">
<h3>useMutationObserver</h3>
<div ref="el" class="mutation-container">
<div>初始元素</div>
</div>
<button @click="addElement">添加元素</button>
<div class="mutation-log">
<h4>DOM 变化日志:</h4>
<ul>
<li v-for="(msg, index) in messages" :key="index">{{ msg }}</li>
</ul>
</div>
</div>
</template>
<style>
.mutation-container {
border: 1px solid #ddd;
padding: 16px;
margin-bottom: 16px;
min-height: 50px;
}
.mutation-log {
max-height: 150px;
overflow-y: auto;
border: 1px solid #eee;
padding: 8px;
margin-top: 16px;
}
</style>
💉 12. createInjectionState - 创建注入状态
<script setup>
// 在共享状态文件中
import { createInjectionState } from '@vueuse/core'
const [useProviderCounter, useConsumerCounter] = createInjectionState(
(initialValue) => {
const count = ref(initialValue)
const increment = () => count.value++
return { count, increment }
}
)
// 在父组件中
import { useProviderCounter } from './counterState'
useProviderCounter(0)
// 在子组件中
import { useConsumerCounter } from './counterState'
const { count, increment } = useConsumerCounter()
</script>
<template>
<div class="card">
<h3>createInjectionState</h3>
<p>此功能需在多个组件间使用,请查看代码示例</p>
</div>
</template>
🔄 13. useToggle - 切换布尔值
<script setup>
import { useToggle } from '@vueuse/core'
const [value, toggle] = useToggle(false)
</script>
<template>
<div class="card">
<h3>useToggle</h3>
<p>当前状态: {{ value ? '开启' : '关闭' }}</p>
<button @click="toggle()">切换</button>
<button @click="toggle(true)">设置为开启</button>
<button @click="toggle(false)">设置为关闭</button>
</div>
</template>
🔍 14. useResizeObserver - 监听元素尺寸变化
<script setup>
import { ref } from 'vue'
import { useResizeObserver } from '@vueuse/core'
const el = ref(null)
const width = ref(0)
const height = ref(0)
useResizeObserver(el, (entries) => {
const entry = entries[0]
width.value = entry.contentRect.width
height.value = entry.contentRect.height
})
</script>
<template>
<div class="card">
<h3>useResizeObserver</h3>
<div ref="el" class="resizable-box">
<p>调整此元素大小</p>
<p>宽度: {{ width }}px</p>
<p>高度: {{ height }}px</p>
</div>
</div>
</template>
<style>
.resizable-box {
resize: both;
overflow: auto;
background: var(--color-primary);
color: white;
padding: 16px;
border-radius: 8px;
min-height: 100px;
min-width: 150px;
}
</style>
15. useMouse - 跟踪鼠标位置
<script setup>
import { useMouse } from '@vueuse/core'
const { x, y, sourceType } = useMouse()
</script>
<template>
<div class="card">
<h3>useMouse</h3>
<p>鼠标位置: ({{ x }}, {{ y }})</p>
<p>来源: {{ sourceType }}</p>
</div>
</template>
💾 状态持久化
useLocalStorage+ useSessionStorage
-
使用场景:
- 用户偏好设置(主题、字体大小)持久化
- 表单草稿自动保存
- 跨页面状态共享(如购物车)
-
优势:自动同步存储与内存状态,支持 TypeScript 类型声明
<script setup lang="ts">
import { useLocalStorage } from '@vueuse/core'
// 持久化存储用户主题设置(自动同步到 localStorage)
const theme = useLocalStorage('user-theme', 'light', {
serializer: {
parse: (val) => val || 'light',
serialize: (val) => val
}
})
// 临时存储表单草稿(仅当前会话有效)
const formDraft = useSessionStorage('form-draft', {
username: '',
email: ''
})
</script>
<template>
<div class="card">
<h3>useLocalStorage & useSessionStorage</h3>
<div class="setting-item">
<label>主题模式:</label>
<select v-model="theme">
<option value="light">浅色</option>
<option value="dark">深色</option>
</select>
</div>
<div class="draft-info">
<p>表单草稿:{{ formDraft }}</p>
<button @click="formDraft.username = '新用户'">修改草稿</button>
</div>
</div>
</template>
<style>
.setting-item {
margin: 16px 0;
display: flex;
align-items: center;
gap: 12px;
}
.draft-info {
margin-top: 20px;
padding: 12px;
background: #f5f7fa;
border-radius: 8px;
}
</style>
🌐 网络请求
useFetch
-
使用场景:
- 数据列表加载(分页/筛选)
- 接口数据缓存
- 实时数据轮询(结合
refetchInterval)
-
优势:内置加载状态、错误处理、自动重试,支持响应式数据更新
<script setup>
import { useFetch } from '@vueuse/core'
// 获取用户列表(自动缓存,30秒自动刷新)
const { data: users, isLoading, error, refetch } = useFetch('https://api.example.com/users', {
refetchInterval: 30000, // 30秒自动刷新
immediate: true, // 立即执行请求
onResponse: (response) => {
// 转换数据结构
return { data: response.data.map(user => ({ ...user, fullName: `${user.firstName} ${user.lastName}` })) }
}
})
</script>
<template>
<div class="card">
<h3>useFetch</h3>
<div class="control">
<button @click="refetch" :disabled="isLoading">
{{ isLoading ? '加载中...' : '手动刷新' }}
</button>
</div>
<div v-if="error" class="error">{{ error.message }}</div>
<ul v-else-if="users" class="user-list">
<li v-for="user in users.data" :key="user.id">
{{ user.fullName }} - {{ user.email }}
</li>
</ul>
</div>
</template>
<style>
.user-list {
list-style: none;
padding: 0;
margin: 16px 0;
}
.user-list li {
padding: 8px;
border-bottom: 1px solid #eee;
}
</style>
⏱️ 响应式定时器
useInterval
-
使用场景:
- 倒计时组件(如验证码发送)
- 实时数据刷新(如实时行情)
- 动画帧控制
-
优势:自动响应式暂停/恢复,支持动态调整间隔时间
<script setup>
import { ref } from 'vue'
import { useInterval } from '@vueuse/core'
const count = ref(0)
const remaining = ref(60) // 验证码倒计时60秒
// 基础计数器(每1秒递增)
useInterval(() => {
count.value++
}, 1000)
// 带条件的倒计时(剩余时间>0时执行)
useInterval(() => {
if (remaining.value > 0) {
remaining.value--
}
}, 1000, { immediate: true })
</script>
<template>
<div class="card">
<h3>useInterval</h3>
<p>基础计数器:{{ count }}</p>
<p class="countdown">验证码剩余时间:{{ remaining }}秒</p>
<button @click="remaining = 60">重置倒计时</button>
</div>
</template>
<style>
.countdown {
color: #ff4d4f;
font-weight: bold;
margin: 12px 0;
}
</style>
🚦 条件执行
useThrottle
-
使用场景:
- 滚动事件处理(如无限滚动加载)
- 窗口 resize 事件优化
- 高频点击按钮限制
-
优势:精确控制执行频率,支持 trailing/leading 模式
<script setup>
import { ref } from 'vue'
import { useThrottle } from '@vueuse/core'
const scrollCount = ref(0)
const throttledScroll = useThrottle(() => {
scrollCount.value++
}, 500, { trailing: true }) // 每500ms最多执行一次,触发结束后执行
// 监听窗口滚动
window.addEventListener('scroll', throttledScroll)
</script>
<template>
<div class="card" style="height: 300px; overflow: auto;">
<div style="height: 1000px; display: flex; align-items: center; justify-content: center;">
<p>滚动测试区域</p>
<p>滚动次数(500ms节流):{{ scrollCount }}</p>
</div>
</div>
</template>
🔐 浏览器权限
usePermission
-
使用场景:
- 检查摄像头/麦克风访问权限
- 地理位置权限状态提示
- 通知权限请求引导
-
优势:自动监听权限变更,返回标准化状态(granted/denied/prompt)
<script setup>
import { ref } from 'vue'
import { usePermission } from '@vueuse/core'
// 检查地理位置权限
const geolocation = usePermission('geolocation')
// 检查通知权限
const notification = usePermission('notifications')
</script>
<template>
<div class="card">
<h3>usePermission</h3>
<div class="permission-item">
<h4>地理位置权限:</h4>
<p v-if="geolocation === 'granted'">已授权</p>
<p v-else-if="geolocation === 'denied'">已拒绝</p>
<p v-else>未询问(点击按钮请求权限)</p>
<button @click="navigator.geolocation.getCurrentPosition(() => {}, () => {})">
请求地理位置权限
</button>
</div>
<div class="permission-item">
<h4>通知权限:</h4>
<p v-if="notification === 'granted'">已授权</p>
<p v-else-if="notification === 'denied'">已拒绝</p>
<p v-else>未询问(点击按钮请求权限)</p>
<button @click="Notification.requestPermission()">
请求通知权限
</button>
</div>
</div>
</template>
<style>
.permission-item {
margin: 16px 0;
padding: 12px;
border: 1px solid #eee;
border-radius: 8px;
}
.permission-item button {
margin-top: 8px;
padding: 6px 12px;
}
</style>
📡 实时通信
useWebSocket
-
使用场景:
- 实时聊天应用
- 股票行情推送
- 设备状态监控
-
优势:自动重连、消息队列管理、支持二进制/文本数据
<script setup>
import { ref } from 'vue'
import { useWebSocket } from '@vueuse/core'
const messages = ref([])
const inputText = ref('')
// 连接 WebSocket 服务(示例地址)
const { send, status } = useWebSocket('wss://echo.websocket.org', {
onMessage: (event) => {
messages.value.push(`收到:${event.data}`)
},
autoReconnect: true, // 自动重连
reconnectInterval: 3000, // 3秒重连间隔
heartbeat: {
message: 'ping',
interval: 5000 // 5秒发送心跳
}
})
</script>
<template>
<div class="card">
<h3>useWebSocket</h3>
<div class="status">连接状态:{{ status }}</div>
<div class="message-log">
<div v-for="(msg, index) in messages" :key="index" class="message">
{{ msg }}
</div>
</div>
<div class="input-area">
<input v-model="inputText" placeholder="输入消息..." />
<button @click="send(inputText); inputText = ''">发送</button>
</div>
</div>
</template>
<style>
.status {
padding: 8px;
background: #e6f7ff;
border-radius: 4px;
margin-bottom: 12px;
}
.message-log {
height: 200px;
overflow-y: auto;
margin: 12px 0;
padding: 8px;
background: #f9f9f9;
border-radius: 4px;
}
.message {
padding: 4px 0;
border-bottom: 1px solid #eee;
}
.input-area {
display: flex;
gap: 8px;
margin-top: 12px;
}
.input-area input {
flex: 1;
padding: 6px 12px;
}
</style>
🎨 主题模式
useDark
-
使用场景:
- 系统级暗模式自动跟随
- 手动切换主题模式
- 主题状态持久化
-
优势:自动检测系统颜色方案,支持手动覆盖,提供 CSS 变量注入
<script setup>
import { useDark, useToggle } from '@vueuse/core'
// 检测系统暗模式,支持手动切换
const isDark = useDark({
selector: 'body',
attribute: 'data-theme',
valueDark: 'dark',
valueLight: 'light'
})
const toggleDark = useToggle(isDark)
</script>
<template>
<div class="card">
<h3>useDark</h3>
<p>当前主题:{{ isDark ? '暗模式' : '亮模式' }}</p>
<button @click="toggleDark()">切换主题</button>
<div class="theme-preview">
<div class="light-box">亮模式预览</div>
<div class="dark-box">暗模式预览</div>
</div>
</div>
</template>
<style>
/* 配合 useDark 的 CSS 变量 */
:root {
--color-background: #ffffff;
--color-text: #333333;
}
[data-theme="dark"] {
--color-background: #1a1a1a;
--color-text: #ffffff;
}
.theme-preview {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 16px;
margin-top: 16px;
}
.light-box, .dark-box {
padding: 20px;
border-radius: 8px;
color: var(--color-text);
background: var(--color-background);
}
.dark-box {
opacity: 0.8;
}
</style>
🌍 语言检测
usePreferredLanguages
-
使用场景:
- 国际化应用语言自动匹配
- 内容本地化展示(如日期/数字格式)
- 用户语言偏好统计
-
优势:返回用户浏览器/系统的首选语言列表(按优先级排序)
<script setup>
import { ref, watchEffect } from 'vue'
import { usePreferredLanguages } from '@vueuse/core'
const languages = ref<string[]>([])
const supportedLangs = ['zh-CN', 'en-US', 'ja-JP']
// 监听语言变化
watchEffect(() => {
languages.value = usePreferredLanguages()
})
</script>
<template>
<div class="card">
<h3>usePreferredLanguages</h3>
<p>系统首选语言:</p>
<ul>
<li v-for="(lang, index) in languages" :key="index">
{{ lang }} {{ supportedLangs.includes(lang) ? '(支持)' : '(不支持)' }}
</li>
</ul>
<p class="current-lang">当前应用语言:zh-CN</p>
</div>
</template>
<style>
ul {
list-style: none;
padding: 0;
margin: 12px 0;
}
li {
padding: 4px 0;
color: #666;
}
</style>
🧩 注入状态
createInjectionState
-
使用场景:
- 组件库状态管理(如表格配置、表单校验)
- 跨层级组件状态共享(避免 Prop drilling)
- 插件系统状态隔离
-
优势:提供 Provider/Consumer 模式,支持响应式状态注入
<!-- 父组件:StateProvider.vue -->
<script setup>
import { createInjectionState } from '@vueuse/core'
// 定义状态工厂函数(返回状态和方法)
const createState = () => {
const count = ref(0)
const increment = () => count.value++
return { count, increment }
}
// 创建注入状态(返回 Provider 和 Consumer)
const [useStateProvider, useStateConsumer] = createInjectionState(createState)
// 在当前组件树提供状态
useStateProvider()
</script>
<template>
<div class="card">
<h3>createInjectionState(Provider)</h3>
<button @click="useStateProvider().increment">父组件增加计数</button>
<ChildComponent />
</div>
</template>
<!-- 子组件:ChildComponent.vue -->
<script setup>
import { useStateConsumer } from './StateProvider.vue'
const { count, increment } = useStateConsumer()
</script>
<template>
<div class="child-card">
<h4>子组件状态</h4>
<p>当前计数:{{ count }}</p>
<button @click="increment">子组件增加计数</button>
</div>
</template>
<style>
.child-card {
margin-top: 16px;
padding: 12px;
border: 1px solid #eee;
border-radius: 8px;
}
</style>
总结
VueUse 提供了大量实用的 Composition API,可以显著提高开发效率。本文介绍了 15 个最常用的 hooks:
- useNow - 实时获取当前时间
- useVModel - 简化 v-model 绑定
- useElementVisibility - 检测元素可见性
- useUrlSearchParams - 操作 URL 查询参数
- createGlobalState - 创建全局状态
- useClipboard - 剪贴板操作
- useElementBounding - 获取元素边界信息
- vOnClickOutside - 点击外部事件指令
- useDebounceFn - 防抖函数
- useElementSize - 获取元素尺寸
- useMutationObserver - DOM 变化观察
- createInjectionState - 创建注入状态
- useToggle - 切换布尔值
- useResizeObserver - 监听元素尺寸变化
- useMouse - 跟踪鼠标位置
- useLocalStorage/useSessionStorage - 本地/会话存储持久化
- useFetch - 响应式网络请求
- useInterval - 响应式定时器
- useThrottle - 函数节流控制
- usePermission - 浏览器权限检测
- useWebSocket - 实时 WebSocket 通信
- useDark - 系统暗模式检测与切换
- usePreferredLanguages - 系统首选语言检测
- createInjectionState - 组件注入状态管理
这些 Hooks 进一步扩展了 VueUse 的能力边界,结合官方文档(vueuse.org)可探索更多实用工具,根据具体业务场景灵活选用,大幅提升开发效率。