VueUse useQRCode 指南:在 Vue 3 中声明式生成响应式二维码

原创2026-10-02 17:13:391,455 阅读
文章标签:前端

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
}

可以拆解出三个关键设计:

  1. 参数归一化:toRef 来自 @vueuse/shared 的 toRef 工具,它能把"值 / ref / getter"统一归一化为一个 ref。其实现中,当传入的是函数时会用 customRef 包装成只读 ref;传入 ref 时直接复用;传入普通值则用 ref() 包装。这使得 useQRCode 无论接收何种形式的输入,内部都能以统一的 ref 进行监听。
  2. 响应式监听:watch 监听归一化后的 src,回调为异步函数,并通过 { immediate: true } 在创建后立即执行一次,保证首次渲染就有二维码。当源值变化时,watch 自动触发重新编码,结果写入 shallowRef 中——这也是为什么返回的 ref 会随源自动更新。
  3. 执行条件与 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 即可完成。

登录后查看全文
vueuse