uni-app Vue3 集成uQRCode实现微信支付二维码动态生成与弹窗交互
1. 为什么需要动态生成微信支付二维码在移动支付场景中二维码支付已经成为最主流的支付方式之一。我做过不少uni-app项目发现很多开发者习惯在服务端生成静态二维码图片然后直接返回给前端展示。这种方式虽然简单但存在几个明显问题安全性隐患静态二维码容易被截获和篡改灵活性不足无法实时更新支付状态用户体验差需要额外处理图片加载和错误状态使用uQRCode动态生成二维码的方案可以完美解决这些问题。实测下来这种方案有三大优势实时性订单创建成功后立即生成最新支付链接可控性前端可以灵活控制二维码的显示逻辑性能优化减少不必要的图片请求在Vue3的组合式API环境下我们可以把二维码生成逻辑封装成可复用的composable函数这在多页面需要支付功能的场景下特别实用。2. 环境准备与基础配置2.1 创建uni-app项目首先确保你已经安装HBuilder X最新版创建一个基于Vue3的uni-app项目。我推荐使用typescript模板后续代码提示会更友好# 通过cli创建项目可选 vue create -p dcloudio/uni-preset-vue my-project2.2 安装必要依赖除了uQRCode核心库我们还需要uni-popup弹窗组件来实现优雅的交互体验npm install uqrcodejs dcloudio/uni-ui安装完成后需要在pages.json中配置easycom自动引入组件{ easycom: { autoscan: true, custom: { ^uni-(.*): dcloudio/uni-ui/lib/uni-$1/uni-$1.vue } } }2.3 初始化二维码工具类在utils目录下创建qrcode.js封装基础生成方法import UQRCode from uqrcodejs export const generateQRCode (url, canvasId, size 200) { return new Promise((resolve) { const qr new UQRCode() qr.data url qr.size size qr.make() const ctx uni.createCanvasContext(canvasId) qr.canvasContext ctx qr.drawCanvas() setTimeout(() resolve(), 300) // 确保绘制完成 }) }3. 核心实现流程3.1 订单创建与支付触发在商品页或购物车页我们需要处理订单创建逻辑。这里建议使用Pinia管理支付状态// stores/payment.js export const usePaymentStore defineStore(payment, { state: () ({ paymentUrl: , showQRCode: false }), actions: { async createOrder(params) { try { const res await uni.request({ url: /api/createOrder, method: POST, data: params }) this.paymentUrl res.data.pay_url this.showQRCode true } catch (error) { uni.showToast({ title: 创建订单失败, icon: error }) } } } })3.2 弹窗组件集成使用uni-popup实现居中显示的支付弹窗注意这几个关键配置uni-popup refpopup typecenter :is-mask-clickfalse :safe-areatrue changeonPopupChange view classpopup-content text微信支付/text canvas idqrcode canvas-idqrcode stylewidth: 300rpx; height: 300rpx; / button clickclosePopup取消支付/button /view /uni-popup样式优化建议.popup-content { padding: 40rpx; border-radius: 16rpx; background: #fff; text-align: center; } #qrcode { margin: 20rpx auto; border: 1rpx solid #eee; }3.3 动态生成二维码在弹窗显示时触发二维码生成这里有个关键细节需要确保canvas已经渲染完成。我推荐使用nextTickconst paymentStore usePaymentStore() watch(() paymentStore.showQRCode, async (show) { if (show) { await nextTick() await generateQRCode( paymentStore.paymentUrl, qrcode ) popup.value.open(center) } })4. 高级优化技巧4.1 二维码状态管理实际项目中需要考虑这些状态生成中已生成已过期支付成功建议使用枚举管理状态const QRCodeStatus { INIT: 0, GENERATING: 1, VALID: 2, EXPIRED: 3, PAID: 4 }4.2 自动关闭与跳转支付成功后通常需要自动跳转可以通过轮询或WebSocket实现const checkPaymentStatus async (orderId) { const timer setInterval(async () { const res await checkOrderStatus(orderId) if (res.paid) { clearInterval(timer) popup.value.close() uni.navigateTo({ url: /pages/order/detail?id orderId }) } }, 3000) onUnmounted(() clearInterval(timer)) }4.3 性能优化建议防抖处理防止重复生成二维码缓存机制短时间内重复支付可复用已有二维码错误处理网络异常时的降级方案const generateQRCode _.debounce(async (url) { // ...生成逻辑 }, 500)5. 常见问题排查5.1 二维码显示空白可能原因及解决方案Canvas上下文获取失败确保canvas-id与代码中一致绘制时机问题在onReady或nextTick后执行内容过长微信支付URL超过uQRCode限制时可先编码5.2 弹窗显示异常典型问题包括弹窗位置偏移检查父元素定位点击穿透设置合适的mask-click属性样式冲突使用scoped样式或深度选择器5.3 支付流程中断建议添加这些兜底逻辑二维码过期自动刷新支付超时提醒返回按钮拦截onBackPress(() { if (paymentStore.showQRCode) { paymentStore.showQRCode false return true } })6. 完整实现示例下面是一个整合所有功能的示例组件script setup import { ref, watch } from vue import { generateQRCode } from /utils/qrcode import { usePaymentStore } from /stores/payment const paymentStore usePaymentStore() const popup ref(null) watch(() paymentStore.showQRCode, async (show) { if (show) { await nextTick() await generateQRCode( paymentStore.paymentUrl, qrcode ) popup.value.open(center) checkPaymentStatus() } }) const closePopup () { popup.value.close() paymentStore.showQRCode false } /script template button clickpaymentStore.createOrder() 立即支付 /button uni-popup refpopup typecenter changeonPopupChange !-- 弹窗内容 -- /uni-popup /template样式部分需要注意多端适配特别是iOS和Android的显示差异。建议使用rpx单位并测试不同尺寸设备的显示效果。