十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

React Native三方库鸿蒙化实战:基于react-native-qrcode-svg集成二维码生成

React Native三方库鸿蒙化实战:基于react-native-qrcode-svg集成二维码生成 做了几年 React Native说实话最怕听到的不是需求改版而是“我们要上鸿蒙了”。这一句话背后意味着所有带原生代码的三方库基本都要重新过一遍鸿蒙化适配而你根本不知道哪个库能在预期时间内跑通。今天分享的这个实战项目是我把一个很常见的二维码生成库react-native-qrcode-svg集成进 ReactNative for Harmony 项目下文简称 RNOH的全过程。它属于典型的“看着简单、实际有坑”的三方库鸿蒙化案例——纯 JS 实现理论上改造成本极低但因为它依赖了 react-native-svg 这个带原生渲染的兄弟库链路一下子就被拉长了。这篇文章会把我当时怎么选型、怎么排查版本关系、怎么处理 Turborepo monorepo 下的 Metro 配置、以及踩过的几个运行时问题全部摊开来讲。适合正好在做 RNOH 项目、需要在 Harmony 侧生成或展示二维码、或者正准备给项目里的纯 JS 三方库做鸿蒙集成的人参考。我尽量不摆教科书姿势直接按我实际操作的顺序写。1. 三方库鸿蒙化为什么先从纯 JS 库下手1.1 原生代码是鸿蒙化最大的成本没有之一做三方库鸿蒙化第一件事不是打开 IDE而是先拆解这个库的构成。一个 React Native 三方库按实现方式基本可以分成三类纯 JavaScript 库、带原生 Native Module 的库、以及基于 TurboModule 或者 Fabric 新架构的库。后两者到了 Harmony 侧Native 部分要用 ArkTS 或者 C 重新实现还要处理 JSI 上下文、对象映射、生命周期绑定这些底层逻辑——不是不能做而是工作量完全不可控。我在另一个项目里适配过一个带原生相机扫码的三方库光是把相机的生命周期和 RN 的视图层级对起来就耗了两周。所以真正理性的做法是能用纯 JS 解决的绝对不碰原生。react-native-qrcode-svg 恰好就是这样。它的核心逻辑是二维码数据的编码和矩阵生成这部分是用纯 JS 完成的对外渲染输出用的是 SVG 图形描述不涉及位图操作、不涉及相机、不涉及传感器。从鸿蒙化的角度来看它没有自己实现任何原生模块全部的风险都会集中在它的依赖上。这个判断后来被验证是对的我在集成过程中遇到的所有编译期和运行期问题几乎都来自 react-native-svg而不是 react-native-qrcode-svg 本身。1.2 为什么是 react-native-qrcode-svg而不是其他方案选择二维码方案时我列过几个备选项这里直接说结论react-native-qrcode-scanner 这类库虽然功能全但底层依赖相机原生模块鸿蒙化成本极高我直接放弃。rn-qr-generator 这类库本质是调用原生生成图片再回传 base64同样有原生链路。真正可以考虑的其实就是 react-native-qrcode-svg 和 ArkUI 原生 QRCode 组件。方案是否依赖原生鸿蒙化成本渲染效果适合场景react-native-qrcode-svg依赖 react-native-svg 原生渲染但无自研原生代码中等需适配 svg 库矢量渲染任意尺寸清晰RN 页面内直接展示、支持 logo、渐变原生 ArkUI QRCode 组件强依赖 ArkUI低但要写原生混合层系统级渲染功能简单纯原生页面或能接受混合开发相机扫码方案依赖相机原生模块极高依赖硬件需要扫描场景最终我选了 react-native-qrcode-svg。理由很实际二维码在 App 里最常见的场景是“生成后展示”和“生成后分享”只要能拿到 SVG 节点或者导出图片就够用了。矢量渲染意味着无论用户在小屏还是平板上放大查看边缘都不会出现锯齿。而且它支持 logo 居中、前景色渐变、背景色透明、容错级别可调这些参数对一个业务型 App 来说已经绰绰有余。1.3 先搞清楚它的依赖树再动手react-native-qrcode-svg 的依赖其实很克制。它的核心依赖是 react-native-svg渲染时用的是 SVG 里的 Path、Rect、Defs 这些基础元素。它还有一个隐藏依赖是二维码编码库用来把字符串内容编码成二维码矩阵这部分是 npm 包内嵌的不需要额外安装。这里有一个容易忽略的点虽然在 Harmony 上找不到原版 react-native-svg 的原生实现但 RNOH 社区维护了对应的鸿蒙化版本包名通常在react-native-oh-tpl/这个 scope 下。所以鸿蒙化集成的工作本质上就是“把 react-native-qrcode-svg 的 JS 部分装好再把 react-native-svg 替换成鸿蒙化版本并保证两个库的版本能被 Metro 正确解析”。听起来不复杂但实际操作中版本对齐和构建链路会给你上一课。2. 动手前的版本与依赖检查2.1 RN、RNOH 与 Harmony SDK 的三角关系先锁基线鸿蒙化项目里最先要命的不是代码而是版本。React Native 版本、RNOH 版本、Harmony SDK 基线这三者必须对齐。我们项目当时的 Harmony SDK 基线是 csr harmony 2.1.63.0 原版也就是没有打过任何私有补丁的官方原版。这里有个很隐蔽的误区很多人只看 RNOH 包的版本号不看它对应编译的 SDK 基线。实际上 2.1.63.0 这个数字影响的不是表面的 API 数量而是 ArkTS runtime 的底层行为。三方库的原生实现如果编译时用了较新的 SDK API在旧基线上轻则功能异常重则直接崩。我整理过一个简单的版本矩阵方便排查问题。不同 RNOH 版本的适配情况不完全一样这里以 RN 0.72 系为例项目版本要求React Native0.72.xreact-native-harmony与 RN 大版本保持一致0.72.xHarmony SDKAPI 12 及以上我们锁的是 csr harmony 2.1.63.0 原版react-native-oh-tpl/react-native-svg13.x需与 RN 版本配套react-native-qrcode-svg6.2.x这个矩阵的价值在于一旦运行期出现莫名其妙的问题先拿这个表逐项核对而不是一上来就怀疑是代码写错了。2.2 react-native-svg 在 Harmony 侧的鸿蒙化实现要理解 react-native-svg 的鸿蒙化实现得先明白它和普通 npm 包的区别。原版 react-native-svg 在 Android/iOS 上自带 Native 代码通过 JSI 或旧版的 Native Module 机制把 SVG 元素映射到各自的图形引擎。到了 Harmony 侧这套原生实现自然是不存在的所以必须使用react-native-oh-tpl/react-native-svg这个鸿蒙化版本。这里我要特别强调一个操作思路不要试图把代码里所有import ... from react-native-svg改成react-native-oh-tpl/react-native-svg。这样不仅改动面大而且 react-native-qrcode-svg 内部也是按react-native-svg这个包名去 require 的你根本改不到它的内部代码。正确做法是用 npm alias 解决让react-native-svg这个包名直接指向鸿蒙化版本。这样你的业务代码、三方库内部代码、Native 构建链路都能统一命中鸿蒙化实现不会出现 JS 层和原生层各用各的版本这种诡异问题。2.3 先做最小验证再叠加业务在集成 react-native-qrcode-svg 之前我建议先做一个最小验证单独安装鸿蒙化版本的 react-native-svg在 Harmony 模拟器上渲染一个简单的Rect或者Circle。这一步很多人会跳过但恰恰是最省时间的。我当时先渲染了一个 200x200 的矩形确认 SVG 基础元素能在 Harmony 上正常输出然后尝试加LinearGradient渐变确认画布颜色处理没问题最后才把 react-native-qrcode-svg 接进来。这样一旦后续二维码渲染出问题我可以立刻排除“svg 基础能力不行”这个因素缩小排查范围。如果一上来就整个链路一起跑报错信息会互相干扰排查效率很低。3. 集成实战从安装到跑通3.1 安装包与 npm alias 的落地操作安装环节看似简单但有几个细节值得注意。我用的包管理工具是 yarn执行命令如下yarn add react-native-qrcode-svg yarn add react-native-svgnpm:react-native-oh-tpl/react-native-svg^13.14.0第二条命令就是上面说的 npm alias。安装完成后package.json 里看起来会是这样的状态{ dependencies: { react-native: 0.72.12, react-native-qrcode-svg: ^6.2.0, react-native-svg: npm:react-native-oh-tpl/react-native-svg^13.14.0 } }注意这里 react-native-svg 的版本号 13.14.0 是 react-native-svg 原生开源仓库的版本号而不是react-native-oh-tpl这个 npm 包自己的版本号。鸿蒙化仓库通常沿用上游版本号方便开发者对照。安装完之后我习惯先跑一遍yarn install然后看 lock 文件里 react-native-svg 的实际解析路径确认它确实指向了鸿蒙化包。这一步能避免后面 Metro 打包时解析到 npm 源上的原版包。3.2 Turborepo 场景下 Metro 配置怎么处理我们项目是 monorepo 结构包管理和任务编排用的是 Turborepo。React Native 项目在 monorepo 里最经典的问题就是 Metro 找不到依赖。如果你把 Harmony 入口 App 放在apps/harmonyApp把共享的 RN 库放在packages/rn-libs那 Metro 默认只会从apps/harmonyApp/node_modules往上逐级查找一旦某个依赖被 Turborepo 提升到仓库根目录Metro 就会解析失败。我当时的 metro.config.js 大致长这样const path require(path); const { getDefaultConfig } require(react-native/metro-config); const config getDefaultConfig(__dirname); config.watchFolders [ path.resolve(__dirname, ../../), ]; config.resolver.nodeModulesPaths [ path.resolve(__dirname, node_modules), path.resolve(__dirname, ../../node_modules), ]; config.resolver.sourceExts [ ...config.resolver.sourceExts, harmony.ts, harmony.tsx, ]; module.exports config;这段配置里watchFolders让 Metro 能监听到整个 monorepo 的文件变化避免改了共享库代码后缓存不生效。nodeModulesPaths则明确告诉 Metro 去哪里找依赖。这两个配置缺一不可只配 watchFolders 不配 nodeModulesPaths打包时会偶发“模块找不到”只配 nodeModulesPaths 不配 watchFolders开发时热更新会失效。Turborepo 这边的任务编排也要同步调整。我在 turbo.json 里给 harmony App 加了独立的 bundle 任务确保构建顺序是先 build 共享库再 bundle 入口 App{ pipeline: { build: [^build], bundle:harmony: [^build] } }然后在 Harmony App 的 package.json 里加脚本bundle:harmony: turbo run bundle:harmony。Turborepo 的作用就是保证 monorepo 里依赖顺序正确避免出现“App 先打包、共享库还没 build”这种低级问题。3.3 核心代码QRCode 组件怎么用依赖装好、Metro 配好之后就到了最快乐的环节写业务代码。react-native-qrcode-svg 的使用非常简单核心是一个QRCode /组件。我写了一个带 logo 和渐变效果的示例import React, { useRef } from react; import { View, Text, TouchableOpacity } from react-native; import QRCode from react-native-qrcode-svg; export function QrPanel({ content }: { content: string }) { const qrRef useRefany(null); const handleExport () { // 导出 base64 图片用于分享或保存 qrRef.current?.toDataURL((dataURL: string) { console.log(qr data url length:, dataURL.length); }); }; return ( View style{{ alignItems: center, padding: 16 }} QRCode ref{qrRef} value{content} size{220} color#1A1A1A backgroundColor#FFFFFF eclM quietZone{12} logo{require(../assets/logo.png)} logoSize{40} logoBackgroundColor#FFFFFF logoMargin{4} logoBorderRadius{8} enableLinearGradient linearGradient{[#1890FF, #36CFC9]} / TouchableOpacity onPress{handleExport} Text导出二维码图片/Text /TouchableOpacity /View ); }这里对几个关键参数做一个说明方便你按需调整参数作用我的建议value二维码承载的内容可以是链接、文本、JSON 字符串长度越长矩阵越密建议控制内容量ecl纠错级别L/M/Q/H 从低到高带 logo 时至少用 M否则扫码容易失败quietZone二维码四周的留白区建议设置 8 以上很多扫码 App 对留白敏感logo 系列参数在二维码中心嵌入 logologo 会遮挡数据务必配合高纠错级别enableLinearGradient是否启用前景渐变视觉好看但要确认扫码机能识别特别提醒一点HTTPS 链接通常比纯文本更稳因为多数扫码工具对纯文本的解析策略差异很大。如果二维码用于支付、登录这类强业务场景我用的是 H 级纠错虽然矩阵会更密但容错能力更强。ref和toDataURL是另一个重点。react-native-qrcode-svg 在渲染时会把内部的 SVG 组件挂到 ref 上通过toDataURL可以把当前二维码导出为 base64 图片。Harmony 侧我实测这个方法是可用的不需要额外权限。这里注意回调是异步的返回值是 data URL 字符串别当同步方法用。3.4 原生侧注册与重新构建JS 层代码写完之后还有一个步骤是很多人容易漏掉的原生侧注册。虽然 react-native-qrcode-svg 没有原生代码但它的宿主依赖 react-native-svg 鸿蒙化版本里有原生实现。RNOH 项目里原生包需要通过 PackageProvider 注册到应用里。在entry/src/main/ets/下找到项目自己的 PackageProvider 文件加入ReactNativeSvgPackageimport { ReactNativeSvgPackage } from react-native-oh-tpl/react-native-svg; export class PackageProvider implements IPackageProvider { getPackages(): RNPackage[] { return [ new ReactNativeSvgPackage(), ]; } }这里有个非常重要的实操经验加入原生包之后必须做一次完整的原生构建而不是仅仅刷新 Metro。很多第一次接触 RNOH 的开发者会习惯性以为 RN 项目改完 JS 代码Metro 一刷新就够了。但原生模块的注册发生在 App 启动阶段不重新编译 Harmony 侧的 hap 包原生代码根本不会进到产物里。我当时就是漏了这一步浪费了大半天排查一个实际上并不存在的 JS 问题。构建命令取决于你们项目的 hvigor 配置通常会走 DevEco Studio 的同步和构建入口也可以在命令行执行。构建完成后重新安装 hap 到模拟器或真机再启动 Metro。启动 Metro 时我习惯加--reset-cache尤其是刚配完 metro.config.js 的情况下旧缓存会掩盖很多问题。npx react-native start --reset-cache4. 踩坑实录与排查方法4.1 运行期报错SvgView 未注册或找不到 RNGV这是我遇到的第一个真正的运行期问题。现象是 App 能启动但一旦渲染 QRCode 组件页面直接红屏错误信息大意是找不到 react-native-svg 的原生实现。排查思路按顺序走先确认 react-native-svg 是否真的安装成了鸿蒙化版本这一步看 package.json 和 lock 文件再确认 PackageProvider 是否注册了ReactNativeSvgPackage最后确认是否重新构建了 hap 包。我当时的根因就是 PackageProvider 里漏了注册属于最低级的错误。如果以上都排查完还是报错那基本可以锁定是版本对齐问题。检查react-native-oh-tpl/react-native-svg的版本是不是和你 RNOH 的版本配套。鸿蒙化包的版本矩阵在官方仓库有说明不要自己凭感觉组合。4.2 二维码渲染出来是空白但没有报错这个坑比直接报错更让人头疼。现象是页面正常渲染了QRCode 组件占位也有了但就是一片空白。当时我第一反应是样式问题于是给容器加了背景色结果还是一样。最后定位到的问题很有意思enableLinearGradient这个属性在 Harmony 侧的 SVG 渐变处理上偶发兼容问题。二维码数据矩阵本身是用多段 Path 渲染的当渐变遮罩的坐标计算在部分 RNOH 版本上有误差时Path 的填充会被整体带偏表现出来就是“看似空白”。解决办法有两个一是先用enableLinearGradient{false}做验证排除渐变问题二是升级或降级react-native-oh-tpl/react-native-svg的版本找到当前 RNOH 版本下渐变正常的那个点。我最后用纯色方案不影响业务也规避了兼容风险。4.3 Harmony 真机上偶发白屏模拟器正常模拟器一切正常真机上却偶发白屏这是我在交付前碰到的最后一个问题。排查后确认是版本一致性导致的我们某台测试机上的 harmony hap 包是旧的构建产物里面的 react-native-svg 原生实现版本和 JS 层实际调用的版本不一致导致渲染线程在初始化时异常。复现路径往往是“改了一版代码只刷了 Metro没有重新构建安装 hap”。这个问题的解法没有捷径只能统一发布流程每次 JS 代码变更后要么走完整的构建发布链路要么至少保证参与测试的所有设备安装的是同一批构建产物。我在项目里加了一条发布检查清单JetBrains 风格的那种纯文本写到仓库里每次发版前逐项打勾。4.4 常见问题速查表把这段时间遇到并解决的问题整理成一张速查表方便遇到同类问题的人直接对号入座症状根因解决方案运行时报 SvgView/RNGV 未注册react-native-svg 原生包未注册或装成了原版而非鸿蒙化版本检查 npm alias、检查 PackageProvider、重新构建二维码空白但无报错渐变兼容问题先禁用 enableLinearGradient 验证再调整 svg 版本模拟器正常真机白屏hap 构建产物与 JS 版本不一致统一构建与安装流程避免只刷 MetroMetro 提示找不到 react-native-svgmonorepo 下依赖提升导致解析失败配置 watchFolders 和 nodeModulesPathstoDataURL 拿不到回调在组件未完成初次渲染时就调用通过 onLayout 或延迟调用确保渲染完成5. 个人实操心得5.1 关于工程结构和方法论的两个建议第一个建议是鸿蒙化的三方库集成一定要建立版本矩阵文档。RNOH 项目里RN、RNOH、Harmony SDK、三方库鸿蒙化版本是一张复杂的依赖网任何一个环节出错都可能在很晚才暴露。这份文档不需要写得多优美把项目实际验证过的版本组合列清楚能节省整个团队的时间。我后来所有涉及 RNOH 的新项目第一件事就是先看版本矩阵再写业务代码。第二个建议是纯 JS 三方库鸿蒙化优先走“最小验证 分步叠加”的路径。不要一口气把整个业务功能接上再调试。每加一个依赖就单独验证一次它在 Harmony 侧是否能跑通。这样即使出了问题定位范围也会被压缩到最小。这个方法论听起来老生常谈但在鸿蒙化这个充满未知的领域里是最有效的。5.2 后续扩展二维码组件还能怎么玩react-native-qrcode-svg 集成跑通之后还可以围绕它做一些扩展。我在项目中顺手实现了两个小功能一个是通过toDataURL把二维码图片分享到系统相册另一个是根据页面主题动态切换二维码的前景色和背景色。这些扩展都不需要再引入新的原生依赖全部在 JS 层搞定。如果你在项目里需要批量生成二维码也可以直接调用底层的二维码编码函数先把矩阵算出来再决定用 SVG 渲染还是导出位图灵活度很高。最后再分享一个小细节我在 Harmony 真机上测过同一张二维码SVG 渲染的清晰度确实比位图方案好一个档次尤其是在系统设置里开启字体放大之后位图会跟着明显拉伸模糊而 SVG 始终是矢量输出。这个体验细节值得你在技术选型时多权衡一下。
返回列表