VueUse useQRCode 指南:在 Vue 3 中声明式生成响应式二维码
VueUse useQRCode 指南:在 Vue 3 中声明式生成响应式二维码
useQRCode 是 VueUse @vueuse/integrations 包中为 qrcode 库提供的响应式封装。它把"文本 → 二维码 Data URL"这一转换过程封装成组合式函数(Composable),让开发者可以用几行代码在 Vue 3 应用中生成二维码,并且二维码会随着绑定源(静态字符串、ref 或 getter)的更新而自动重新生成。阅读本文后,你将掌握 useQRCode 的安装方式、两种基本用法、可选参数(如纠错等级与边距)以及其底层源码实现原理。
背景:@vueuse/integrations 中的集成型工具
useQRCode 并不属于 VueUse 核心包 @vueuse/core,而是位于专门负责"集成第三方工具库"的 @vueuse/integrations 包中。该包通过 peerDependencies 声明了一组可选的第三方依赖(async-validator、axios、qrcode、sortablejs 等),并在 package.json 中将它们全部标记为 optional: true——这意味着你不安装 qrcode 也能使用该包中的其他函数,只有在使用 useQRCode 时才需要单独安装二维码库。
从 integrations 包入口文件 可以看到,useQRCode 与其他 11 个集成函数一起被统一导出:
export * from './useAsyncValidator'
// ...
export * from './useQRCode'
export * from './useSortable'
安装
useQRCode 本身是 @vueuse/integrations 的一部分,而真正负责编码的是 qrcode 库。因此安装分为两步:
npm i @vueuse/integrations
npm i qrcode@^1
在 integrations 包的 package.json 中,qrcode 被声明为 peerDependencies(版本范围为 ^1.5)且为可选依赖,同时在 devDependencies 中提供了 @types/qrcode 用于 TypeScript 类型支持。项目本身使用 pnpm 工作区管理(见 pnpm-workspace.yaml),在仓库内可以通过 pnpm add 安装。
基本用法:传入普通字符串
最简单的场景是把一段静态文本(或组件里的普通字符串变量)直接传给 useQRCode:
import { useQRCode } from '@vueuse/integrations/useQRCode'
// `qrcode` 是一个包含 Data URL 字符串的 ref
const qrcode = useQRCode('text-to-encode')
返回的 qrcode 是一个 shallowRef<string>,其值是一段 data:image/png;base64,... 形式的图片地址,可以直接绑定到 <img> 标签的 src 上:
<img :src="qrcode" alt="QR Code" />
响应式用法:传入 ref 或 getter
useQRCode 的第一个参数类型是 MaybeRefOrGetter<string>,这意味着你既可以传普通值,也可以传一个 Vue ref 甚至是一个返回字符串的函数(getter)。当传入 ref 时,二维码会随着源 ref 的变化自动重新生成:
import { useQRCode } from '@vueuse/integrations/useQRCode'
import { shallowRef } from 'vue'
const text = shallowRef('text-to-encode')
const qrcode = useQRCode(text)
配合表单输入即可实现"边输入边生成"的效果:
<input v-model="text" type="text" />
<img :src="qrcode" alt="QR Code" />
传递生成选项:纠错等级与边距等
useQRCode 的第二个参数 options 的类型是 QRCode.QRCodeToDataURLOptions,它来自 @types/qrcode 类型声明,并会被原样透传给底层 QRCode.toDataURL。仓库自带的 demo.vue 给出了一个真实可运行的选项示例:
import { useQRCode } from '@vueuse/integrations/useQRCode'
import { shallowRef } from 'vue'
const text = shallowRef('https://vueuse.org')
const qrcode = useQRCode(text, {
errorCorrectionLevel: 'H',
margin: 3,
})
其中 errorCorrectionLevel: 'H' 表示最高纠错等级(即使二维码部分破损仍可扫描),margin: 3 表示二维码四周保留 3 个模块宽度的边距。该 demo 还展示了响应式场景下"源文本为空时不展示图片"的常见处理:v-if="text" 包裹 <img>,避免渲染空二维码。这类选项的完整取值范围(如纠错等级 L/M/Q/H、输出尺寸 width、颜色 color 等)由 qrcode 库及其类型定义提供,可在编码需求变化时按需调整。
源码剖析:它到底做了什么
useQRCode 的完整实现非常精简,全部逻辑都在 packages/integrations/useQRCode/index.ts 中:
import type { MaybeRefOrGetter } from 'vue'
import { isClient, toRef } from '@vueuse/shared'
import QRCode from 'qrcode'
import { shallowRef, watch } from 'vue'
export function useQRCode(
text: MaybeRefOrGetter<string>,
options?: QRCode.QRCodeToDataURLOptions,
) {
const src = toRef(text)
const result = shallowRef('')
watch(
src,
async (value) => {
if (src.value && isClient)
result.value = await QRCode.toDataURL(value, options)
},
{ immediate: true },
)
return result
}
可以拆解出三个关键设计:
- 参数归一化:
toRef来自 @vueuse/shared 的 toRef 工具,它能把"值 / ref / getter"统一归一化为一个 ref。其实现中,当传入的是函数时会用customRef包装成只读 ref;传入 ref 时直接复用;传入普通值则用ref()包装。这使得useQRCode无论接收何种形式的输入,内部都能以统一的 ref 进行监听。 - 响应式监听:
watch监听归一化后的src,回调为异步函数,并通过{ immediate: true }在创建后立即执行一次,保证首次渲染就有二维码。当源值变化时,watch 自动触发重新编码,结果写入shallowRef中——这也是为什么返回的 ref 会随源自动更新。 - 执行条件与 SSR 安全:回调内部先判断
src.value && isClient才真正调用QRCode.toDataURL。isClient来自@vueuse/shared,用于检测当前运行环境是否为浏览器。空字符串或 falsy 值不会触发编码,避免了生成无效二维码;非浏览器环境(如 SSR/服务端渲染)下则直接跳过,保证了该函数在 Node 端不会因调用浏览器专用 API 而报错。
因此,useQRCode 的底层调用链可概括为:toRef(text) → watch(src, ...) → QRCode.toDataURL(value, options) → result。
体积与按需引入
从仓库的 export-size.md 记录来看,useQRCode 自身的实现体积约为 306 B(gzip 前的产物大小),非常轻量。为了获得更好的 tree-shaking 效果,@vueuse/integrations 的 README 明确建议从子路径导入而不是从包根路径导入:
// 不推荐
import { useAxios } from '@vueuse/integrations'
// 推荐:从子模块导入
import { useQRCode } from '@vueuse/integrations/useQRCode'
这一点在 integrations 包的 package.json 导出映射 中也有体现:每个函数都有独立的导出路径(如 "./useQRCode": "./dist/useQRCode.js"),且包本身声明了 "sideEffects": false,配合构建工具即可实现按需打包。
完整实战示例
结合以上内容,一个完整的"输入即生成"二维码组件可以这样写(参考仓库 demo.vue 的结构):
<script setup lang="ts">
import { useQRCode } from '@vueuse/integrations/useQRCode'
import { shallowRef } from 'vue'
const text = shallowRef('https://vueuse.org')
const qrcode = useQRCode(text, {
errorCorrectionLevel: 'H',
margin: 3,
})
</script>
<template>
<input v-model="text" type="text">
<img v-if="text" :src="qrcode" alt="QR Code">
</template>
这个例子同时覆盖了三种要点:以 shallowRef 作为响应式数据源、通过 options 提升二维码的纠错能力与边距、以及用 v-if 在源文本为空时隐藏图片。在实际项目中,你还可以把生成结果用作分享链接卡片、扫码登录、小程序跳转参数等场景的二维码数据源。
小结
useQRCode 以极小的实现成本(约 300 B)把成熟的 qrcode 库接入 Vue 3 的响应式体系:MaybeRefOrGetter 输入归一化、watch 自动重编码、isClient 保证 SSR 安全、shallowRef 轻量输出。需要生成二维码时,你不再需要手动管理"数据变化 → 重新调用库函数 → 更新状态"的流程,只需一行 useQRCode 即可完成。