在 uni-app 的日常开发中,我们经常需要编写大量工具函数来处理本地存储、系统偏好、网络状态、设备信息等底层能力。虽然 uni-app 已经提供了丰富的 API,但在 Vue3 组合式 API 架构下,直接使用原生 API 往往需要手动管理响应式状态和生命周期,代码逐渐变得零散。此时,VueUse 这个庞大的组合式函数集合能够给我们带来流畅的响应式封装,让许多常见功能变得像调用一个函数一样简单。本文将手把手教你如何在 uni-app 项目里安全地集成 VueUse,并通过三个典型场景展示其带来的开发效率飞跃。
为什么在 uni-app 中选择 VueUse?
VueUse 是 Vue 生态中最为知名的组合式函数库,涵盖了 200 多个开箱即用的函数,包括状态、传感器、浏览器、动画、时间、网络、元素等众多类别。它的最大优势在于:
- 自动响应式绑定:将浏览器或设备 API 包装为响应式引用(ref),状态变化自动触发视图更新。
- 生命周期管理:自动在组件卸载时清理监听器,避免内存泄漏。
- 跨平台兼容:许多函数通过特征检测提供了通用的 polyfill,且在 uni-app 环境中多数浏览器 API 函数仍然适用(尤其在 H5 平台)。
- Tree-shaking 友好:仅打包被使用的函数,对小程序包体积影响小。
在 uni-app 中,我们可以根据运行平台条件化地引入 VueUse 函数,让代码在不同端表现出一致的行为,同时保持组合式 API 的优雅写法。
环境准备与安装配置
首先确保你的 uni-app 项目基于 Vue3 版本(Vue2 不兼容 VueUse)。在项目根目录下执行安装:
npm install @vueuse/core
安装完成后,即可在任意组件或页面中按需导入函数。由于 uni-app 的构建系统基于 Vite(3.x 版本),Tree-shaking 会自动生效。对于某些依赖特定浏览器环境的函数(如 useGeolocation、useLocalStorage 等),需要在条件编译或运行时检查平台。
我们推荐在 main.js 中创建一个自定义的 VueUse 适配层,以统一处理平台差异。但多数常用函数无需额外配置即可在 H5 和 App 平台工作;在小程序中,需要谨慎选择仅使用不依赖 DOM 或 BOM 核心对象的函数,比如 useStorage 和 useDark 都可以通过替换底层存储和查询方法来支持小程序。
案例一:使用 useStorage 实现跨页面持久化设置
uni-app 自带的 uni.setStorageSync 和 uni.getStorageSync 功能完善,但它们是命令式的,且不提供响应式绑定。VueUse 的 useStorage 函数能将本地存储中的数据自动转换为响应式 ref,任何对 ref 的修改都会同步写入存储,反之如果在其它地方修改了存储,也能通过事件监听同步回 ref(需平台支持 storage 事件)。
以下示例使用 useStorage 管理用户偏好设置(如字体大小)。我们首先创建一个自定义的 useStorage 适配层,确保在小程序中使用 uni 的原生 Storage。
创建 composables/useStorage.js:
import { useStorage as useVueStorage } from '@vueuse/core'
import { ref } from 'vue'
// 判断当前平台是否为小程序
const isMp = process.env.VUE_APP_PLATFORM === 'mp-weixin' ||
process.env.VUE_APP_PLATFORM === 'mp-alipay' ||
process.env.VUE_APP_PLATFORM === 'mp-baidu'
export function useStorage(key, defaultValue) {
if (isMp) {
// 小程序环境:手动封装响应式存储
const stored = uni.getStorageSync(key)
const data = ref(stored !== '' && stored !== undefined ? JSON.parse(stored) : defaultValue)
// 监听数据变化,自动写回
const writeBack = () => {
uni.setStorageSync(key, JSON.stringify(data.value))
}
return {
data,
writeBack
}
} else {
// H5 或 App 环境:直接使用 VueUse 的 useStorage(基于 localStorage)
return useVueStorage(key, defaultValue)
}
}
在设置页面 pages/settings/settings.vue 中使用:
<template>
<view>
<text>字体大小:{{ fontSize }}</text>
<button @click="increase">增大</button>
<button @click="decrease">减小</button>
</view>
</template>
<script setup>
import { useStorage } from '@/composables/useStorage'
const { data: fontSize, writeBack } = useStorage('app_font_size', 16)
function increase() {
fontSize.value += 2
if (writeBack) writeBack()
}
function decrease() {
fontSize.value = Math.max(12, fontSize.value - 2)
if (writeBack) writeBack()
}
</script>
在其它页面读取字体设置时,同样使用 useStorage('app_font_size') 即可获得响应式数据,任何页面的改动都会即时反映到所有引用处,同时数据自动持久化。这种统一的响应式状态管理比原生 Storage API 更符合 Vue 的声明式思维。
案例二:使用 useDark 实现自适应深色模式
VueUse 的 useDark 函数可以自动检测用户系统的配色方案偏好(prefers-color-scheme),并提供一个可手动切换的响应式布尔值。在 uni-app 中,H5 平台完全支持该函数;在 App 平台,需要通过条件编译获取原生系统主题;在小程序中,我们可以通过 uni 的 uni.getSystemInfoSync 读取主题并模拟实现。
我们构建一个应用级主题管理模块 composables/useDarkMode.js:
import { useDark, useToggle } from '@vueuse/core'
import { ref } from 'vue'
export function useDarkMode() {
let isDark = ref(false)
let toggleDark = () => {}
// #ifdef H5
const dark = useDark()
isDark = dark.isDark
toggleDark = useToggle(dark.isDark)
// #endif
// #ifdef APP-PLUS
const systemInfo = uni.getSystemInfoSync()
const initialDark = systemInfo.theme === 'dark'
isDark = ref(initialDark)
toggleDark = () => {
isDark.value = !isDark.value
// 在 App 中可通过 plus.nativeObj 等改变界面主题,此处仅改变状态
}
// #endif
// #ifdef MP-WEIXIN
const systemInfo = uni.getSystemInfoSync()
isDark = ref(systemInfo.theme === 'dark')
toggleDark = () => {
isDark.value = !isDark.value
// 小程序中无法直接修改系统主题,但可以切换自定义主题变量
}
// #endif
return { isDark, toggleDark }
}
在全局组件或 App.vue 中使用:
<script setup>
import { useDarkMode } from '@/composables/useDarkMode'
import { watch } from 'vue'
const { isDark, toggleDark } = useDarkMode()
// 将主题状态注入全局样式或通过 pinia 共享
watch(isDark, (val) => {
uni.setStorageSync('theme', val ? 'dark' : 'light')
// 动态改变页面背景等(可配合 CSS 变量)
})
</script>
在任意页面放置主题切换按钮,调用 toggleDark 即可。H5 端会直接反射到 prefers-color-scheme 媒体查询的变更,UI 随之响应;小程序和 App 端则通过我们手动管理的响应式状态实现类似效果。VueUse 的 useDark 为我们统一了接口,让代码尽量简洁。
案例三:使用 useNetwork 检测网络状态并实时提示
VueUse 的 useNetwork 函数封装了 Navigator 的在线/离线事件,返回一个响应式的 isOnline 布尔值。在 uni-app 中,我们可以基于 uni.getNetworkType 和 uni.onNetworkStatusChange 构建类似功能,但 VueUse 在 H5 和 App 环境下直接可用,小程序需要手动封装。
为统一管理,创建 composables/useNetwork.js:
import { useNetwork as useVueNetwork } from '@vueuse/core'
import { ref } from 'vue'
export function useNetwork() {
let isOnline = ref(true)
let networkType = ref('unknown')
// #ifdef H5 || APP-PLUS
const network = useVueNetwork()
isOnline = network.isOnline
networkType = network.type
// #endif
// #ifdef MP-WEIXIN || MP-ALIPAY || MP-BAIDU
// 初始化网络状态
uni.getNetworkType({
success: (res) => {
isOnline.value = res.networkType !== 'none'
networkType.value = res.networkType
}
})
uni.onNetworkStatusChange((res) => {
isOnline.value = res.isConnected
networkType.value = res.networkType
})
// #endif
return { isOnline, networkType }
}
在任意页面或组件中轻松使用:
<template>
<view v-if="!isOnline" class="offline-tip">
当前网络不可用,请检查网络连接
</view>
</template>
<script setup>
import { useNetwork } from '@/composables/useNetwork'
const { isOnline } = useNetwork()
</script>
通过条件编译,我们在不同的平台下透明地提供了统一的响应式网络状态接口。VueUse 的 H5/App 实现基于浏览器事件,可靠性高;小程序的实现通过原生 API 监听,同样有效。
平台兼容性与自定义适配
VueUse 的多数函数在设计时考虑了 SSR 和跨环境,但仍有一部分强依赖浏览器特有对象(如 useClipboard 需要 navigator.clipboard)。在 uni-app 中,可以遵循以下策略确保安全使用:
- 优先使用通用函数:如
useStorage、useDark、useNetwork、useCounter、useIntervalFn等不依赖 DOM 的函数。 - 条件编译隔离:对平台特定的函数用
#ifdef包裹,并为其它平台提供降级方案。 - 创建适配层:像上文案例一样,为关键函数建立统一的
composables封装,内部处理平台差异,对外暴露一致 API。 - 运行时检测:使用
typeof window !== 'undefined'或uni.getSystemInfoSync().platform做运行时判断,动态选择实现。
这种架构让 VueUse 的丰富生态在 uni-app 中最大化利用,同时保障了各平台的可维护性。
总结
通过集成 VueUse,uni-app 开发者可以大幅减少重复的工具函数编写,将更多精力放在业务逻辑上。本文的三个案例——持久化存储、深色模式切换和网络状态检测,展示了如何通过简单的封装,让 VueUse 在不同平台下提供一致的响应式开发体验。VueUse 庞大的函数集就像一座宝库,等待你在 uni-app 项目中去挖掘。
现在,打开你的 uni-app 项目,安装 VueUse 并尝试用 useStorage 替换那些繁琐的 uni.setStorageSync 调用吧,感受组合式函数的魅力。

