尧图精选

airi 项目中的 VueUse useSupported:SSR 安全的浏览器能力检测与响应式降级实战

🕒 发布时间:2026/9/10 22:30:44 📁 来源:尧图网络
airi 项目中的 VueUse useSupportedSSR 安全的浏览器能力检测与响应式降级实战【免费下载链接】airi Self hosted, you-owned Grok Companion, a container of souls of waifu, cyber livings to bring them into our worlds, wishing to achieve Neuro-samas altitude. Capable of realtime voice chat, Minecraft, Factorio playing. Web / macOS / Windows supported.项目地址: https://gitcode.com/GitHub_Trending/ai/airi导读useSupported是 VueUse 工具库中一个体量极小却用途关键的组合式函数composable它的官方定位是SSR compatibilityisSupported——即把当前运行环境是否支持某个 API这一判断封装成响应式引用ComputedRefboolean并天然兼容服务端渲染SSR场景。在 airi 这类同时横跨 Web、Electron 桌面端、Capacitor 移动端stage-pocket与 Tamagotchi 桌面壳的仓库中大量浏览器 API剪贴板、震动、BroadcastChannel、设备枚举、用户媒体等并不能假设在每一端都可用useSupported正是这类能力探测与渐进增强的最佳落点。读完本文你将掌握useSupported的精确用法、底层类型契约以及如何在真实多端项目中用它写出不会在 SSR 或非浏览器环境下崩溃的特性降级逻辑。一、为什么需要useSupportedSSR 与多端环境下的能力探测浏览器 API 从来不是普适的Battery Status API 只在 Chromium 系浏览器可用Vibration API 只在移动端 Safari 与 Android 浏览器可用Clipboard API 的可用性还取决于页面是否处于安全上下文HTTPS。更麻烦的是在 SSR 环境下根本没有navigator、window这类全局对象代码一旦在渲染阶段直接访问它们就会抛出ReferenceError。airi 仓库本身的运行环境矩阵足以说明这个问题apps/stage-web纯 Web 前端部署于 Netlify / Cloudflare Pages见 netlify.toml 与 wrangler.toml但也配置了 Dockerfile 做服务端渲染/托管apps/stage-tamagotchi基于 Electron 的桌面应用见 electron.vite.config.ts渲染进程运行在 Chromium 中但主进程与预加载脚本src/preload没有完整的浏览器全局对象apps/stage-pocketCapacitor 移动端壳见 capacitor.config.ts部分 Web API 被 WebView 实现另一部分被屏蔽。在这种环境下直接写if (navigator.getBattery)是有风险的SSR 阶段没有navigator会直接抛错。而useSupported通过接收一个回调函数、由调用方决定何时安全地访问全局对象的方式把探测逻辑延迟到浏览器端执行从根本上规避了这一问题。二、官方用法与类型契约2.1 最小示例关联文档 给出的核心用法如下import { useSupported } from vueuse/core const isSupported useSupported(() navigator getBattery in navigator) if (isSupported.value) { // do something navigator.getBattery }要点拆解useSupported接收一个回调函数回调返回任意值unknown内部通过Boolean()语义判定真假返回一个ComputedRefboolean即响应式布尔值在模板中可直接v-ifisSupported使用在脚本中用.value读取回调在 Vue 的computed求值上下文中执行这正是它返回ComputedRef的原因意味着探测本身是惰性lazy且带缓存的——只有当读取.value或模板依赖它时才会真正执行探测逻辑。2.2 类型声明逐行解读文档中的完整类型声明为export type UseSupportedReturn ComputedRefboolean export declare function useSupported( callback: () unknown, ): UseSupportedReturncallback: () unknown探测回调。返回任意类型最终会被隐式布尔化。navigator getBattery in navigator这个写法同时完成了两件事先保证navigator存在SSR 安全再检查属性是否存在UseSupportedReturn ComputedRefboolean返回值永远是ComputedRefboolean而不是普通Ref。这意味着探测结果可以参与其他computed的依赖链当探测所依赖的响应式状态变化时结果也会自动更新——虽然通常探测目标如navigator是静态的但这一设计保证了与 Vue 响应式体系的完全融合。2.3 从源码看实现原理从 VueUse 的底层实现逻辑看useSupported本质上是computed(() Boolean(callback()))的封装惰性求值computed只在被访问时才执行回调未访问前不产生任何副作用SSR 安全探测逻辑完全由调用方回调掌控useSupported本身不直接触碰任何全局对象若回调在 SSR 环境被调用navigator为undefinednavigator ...短路求值返回undefinedBoolean(undefined)得到false不会抛错响应式结果参与依赖收集任何使回调返回真值变化的响应式依赖都会触发重新计算。一个常见的写法误区是useSupported(() navigator.getBattery)——这在 SSR 阶段直接访问navigator的成员仍会抛错。正确姿势永远是先判存在、再取成员即回调内部用短路或typeof守护// 安全先判断 navigator 存在 useSupported(() navigator getBattery in navigator) // 等价的安全写法 useSupported(() typeof navigator ! undefined getBattery in navigator)三、SSR 安全的判定模式集锦3.1 判全局对象存在const hasWindow useSupported(() window) // 等价于 hasWindow.value true 时才有 window3.2 判API 属性存在// Web Speech API const speechSupported useSupported(() SpeechRecognition in window || webkitSpeechRecognition in window)3.3 判构造函数可实例化部分 API 需要进一步验证实例化不会抛错可在回调内用try/catch加固const bluetoothSupported useSupported(() { if (!(bluetooth in navigator)) return false try { return !!navigator.bluetooth } catch { return false } })3.4 判媒体类型可枚举结合MediaRecorder.isTypeSupported这类静态方法const opusSupported useSupported(() { return typeof MediaRecorder ! undefined MediaRecorder.isTypeSupported(audio/webm;codecsopus) })四、仓库实战useSupported 与 isSupported 在 airi 中的落地useSupported的直接使用方是无数 VueUse 内部组合式函数useClipboard、useBroadcastChannel、useDevicesList、useWebHaptics、usePermission等它们在返回值中统一暴露isSupported字段。airi 仓库中随处可见对这种能力标记的消费这里给出三处有代表性的源码佐证。4.1 剪贴板about 页与连接设置页apps/stage-tamagotchi/src/renderer/pages/about.vue 中const { copy: copyToClipboard, isSupported: isClipboardSupported } useClipboard()在 apps/stage-tamagotchi/src/renderer/pages/settings/connection/index.vue 中则把isSupported直接用于复制 Token按钮的可用性决策const { copied: authTokenCopied, copy: copyAuthToken, isSupported: isClipboardSupported } useClipboard({ source: authTokenInput, legacy: true }) const canCopyAuthToken computed(() isClipboardSupported.value authTokenInput.value.length 0)这里的模式非常典型isSupported与其他条件一起进入computed组合逻辑剪贴板不可用如非安全上下文时按钮自动禁用绝不触发navigator.clipboard的调用。这正是useSupported返回ComputedRefboolean的核心价值——它可以直接参与派生状态的响应式组合。4.2 设备枚举音频输入的权限请求守卫apps/stage-web/src/composables/audio-input.ts 与 apps/stage-pocket/src/composables/audio-input.ts 中useDevicesList的isSupported被用作权限请求的前置守卫async function request() { if (devices.permissionGranted.value) return if (!devices.isSupported.value) return await devices.ensurePermissions() }在navigator.mediaDevices.enumerateDevices不存在的环境如某些 WebView 或受限上下文中isSupported为falserequest()直接短路返回避免调用根本不存在的ensurePermissions()。这一守卫模式可以推广到任意探测后执行的流程中先看isSupported再动副作用。4.3 广播通道与震动能力标记驱动 UI 展示apps/ui-server-auth/src/pages/verify-email.vue 使用useBroadcastChannel的isSupported判断跨标签页邮箱验证事件通道是否可用apps/stage-web/src/pages/devtools/web-haptics.vue 使用useWebHaptics的isSupported直接在调试页渲染能力状态徽标isSupported: true/false并把能力标记写进class切换图标主题。这两处展示了isSupported的另一种消费方式它本身就是 UI 状态可以驱动条件渲染、状态徽标与图标切换让用户或调试者直观看到当前环境的能力边界。4.4 与仓库内其他 VueUse 函数协同airi 中大量使用 VueUse 组合式函数它们的isSupported均源自useSupported这一统一底座。可对照仓库中的实际引用验证useDevicesList、useUserMediaapps/stage-web/src/composables/audio-input.ts、useClipboard、useBroadcastChannelapps/stage-tamagotchi/src/renderer/composables/model-settings-runtime-snapshot.ts、useLocalStorageapps/stage-tamagotchi/src/renderer/stores/controls-island.ts、useWindowSizeapps/stage-tamagotchi/src/renderer/stores/window.ts等。这也正是仓库中 vueuse-functions 技能 所倡导的实践优先用组合式函数而非手写探测代码保持实现简洁、可维护、高性能。五、使用场景速查表场景推荐写法说明剪贴板复制useClipboard()的isSupported非安全上下文自动降级媒体设备useDevicesList()的isSupported守护权限请求流程跨标签页通信useBroadcastChannel()的isSupported不可用时回退同页事件触觉反馈useWebHaptics()的isSupported桌面端直接隐藏按钮电池信息useSupported(() navigator getBattery in navigator)官方示例场景语音识别useSupported(() SpeechRecognition in window \|\| webkitSpeechRecognition in window)需考虑厂商前缀通知/震动useWebNotification()/useVibrate()的isSupported移动端差异化六、最佳实践与注意事项回调内先判存在、再取成员navigator getBattery in navigator是标准写法不要写useSupported(() navigator.getBattery)SSR 下会抛错。用isSupported守卫副作用凡是先探测后执行的流程都应像 audio-input.ts 那样先检查能力标记再调用 API。isSupported天然是响应式的直接参与computed组合如 connection/index.vue在模板中用v-if/v-show控制 UI。区分useSupported与useCssSupports前者探测 JS API后者探测 CSS 特性CSS.supports都是 SSR 安全的能力探测但作用域不同。探测结果建议模块化复用把公共能力探测提取为独立的组合式函数避免每个组件重复写探测回调。总结useSupported是 VueUse 中小而美的典范一个computed(() Boolean(callback()))的封装同时解决了 SSR 安全、惰性求值与响应式能力标记三个问题。在 airi 这样一个多端Web / Electron / Capacitor / Tamagotchi并存的仓库中它是无数高级组合式函数剪贴板、广播、媒体设备、触觉反馈能力探测的统一底座也是编写渐进增强与优雅降级逻辑的第一道关卡。掌握它的用法与模式是写出健壮跨端 Vue 应用的基础功。【免费下载链接】airi Self hosted, you-owned Grok Companion, a container of souls of waifu, cyber livings to bring them into our worlds, wishing to achieve Neuro-samas altitude. Capable of realtime voice chat, Minecraft, Factorio playing. Web / macOS / Windows supported.项目地址: https://gitcode.com/GitHub_Trending/ai/airi创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
上一篇/下一篇内容由系统自动关联 返回资讯列表 →