本文记录把 react-native-url-polyfill(WHATWG URL 标准实现)适配到 HarmonyOS 的完整过程。
这个库有个特点:它不需要做原生适配 。但"不需要适配"本身就是个需要被证明的结论------而且证明的难点不在"它能不能跑",而在"它跑得对不对"。URL 解析有一份所有人都认的标准语料,这次就靠它把结论钉死了。

一、先说结论
| 项 | 结果 |
|---|---|
| 上游最新版 | 4.0.0(核对 npm registry 的 gitHead,与交付包 spec.json 记的基线 commit 完全一致) |
| 是否需要原生适配 | 不需要 ------没有 harmony/、没有 android/、没有 ios/,也没有自己的原生模块 |
| 是否需要 HAR / 权限 / ohpm 接线 | 都不需要 |
| 依赖 | 无(上游 4.0.0 已把 URL 实现打包进 js/URL.js,20,008 字节) |
| 与 npm 上游的差异 | 零内容改动(逐文件 SHA256 核对,唯一差异是 git checkout 的 CRLF) |
| 自动链接信号 | linked 9 libraries, skipped 1 libraries ------ 计数未增加,库被正确跳过 |
| 编译 | assembleHap 6 分 34 秒;HAP 80,867,224 → 81,260,441 字节 (+393,217 ≈ 384 KB) |
| 设备侧断言 | ✅ 35 / 35 全部通过 |
| WPT 官方一致性语料 | 1580 条用例,通过 1538,失败 42 ;42 条全部 是上游自己声明的已知偏差,未预期偏差 0 |
| 跨引擎一致性 | ✅ 同一份测量代码跑 PC(V8)与设备(Hermes),失败集合差集为 0 |
| 同机对照 | 鸿蒙自带 URL 只过 234 / 1580,这个库过 1538 / 1580 |
一句话结论 :它可以直接用,不需要鸿蒙原生代码;它带来的是"从 234 条到 1538 条"的一致性提升 ------因为鸿蒙平台自带的 URL 是存在但残缺的。
二、判定过程:三步判断一个 RN 库要不要鸿蒙原生适配
判断一个库要不要做原生适配,我固定走三步:
| 步 | 做法 | 这个库的结果 |
|---|---|---|
| ① | npm view <包名> harmony --json,看有没有 harmony.autolinking |
没有 |
| ② | 仓库里有没有 harmony/ 目录(以及 android/、ios/) |
都没有 |
| ③ | 代码里有没有 NativeModules / Platform.OS / requireNativeComponent |
⚠️ 有,3 处 |
第 ① ② 步都指向"纯 JS 库",但第 ③ 步命中了。这时候不能直接下结论------第 ③ 步查到的是"信号",不是"判决"。要接着问一句:
它读的那些平台 API,鸿蒙侧有没有?
把三处平台引用摊开看:
| 文件 | 平台 API | 用途 | 鸿蒙上是否可用 |
|---|---|---|---|
js/URL.js |
NativeModules.BlobModule |
只为 URL.createObjectURL 拼 blob uri |
✅ 存在(实测) |
auto.js |
Platform.OS |
if (Platform.OS !== 'web') setupURLPolyfill() |
✅ harmony |
js/ios10Fix.js |
Platform.Version、Platform.OS |
模块级副作用 :iOS 10 上补 ArrayBuffer.prototype.byteLength |
✅ 不抛错 |
三个都用得上,三个鸿蒙侧都已经有。 所以结论是"不需要原生适配",而不是"它不碰平台"。
这个区分很重要:"纯 JS 库"不等于"零平台耦合"。 判据不是"有没有 platform 代码",而是"它依赖的平台能力鸿蒙侧是否具备"。前者会误判,后者才是真问题。
顺带一个细节:js/ios10Fix.js 是在 index.js 顶部被 import 的,也就是说只要 import 这个库,这段模块级代码就会执行 (读 Platform.Version 后判断 iOS 10)。它在鸿蒙上跑过、没抛错------这一点后来单独写成了断言。
三、这个交付包长什么样:与 npm 上游逐字节一致
既然不做原生适配,那"鸿蒙版"到底改了什么?答案是:实现文件一行没改。
核对方法:拿交付仓库和 react-native-url-polyfill@4.0.0 的 npm tarball(12,696 字节,11 个文件)逐文件比 SHA256。
| 文件 | 与 npm 上游 |
|---|---|
js/URL.js(20,008 字节,SHA256 758313FF...) |
逐字节一致 |
js/URLSearchParams.js |
逐字节一致 |
index.js / auto.js / index.d.ts / js/ios10Fix.js / LICENSE |
内容一致 ,差异仅 CRLF vs LF |
package.json |
交付方改过(加了 files、把 repository 指向 AtomGit 等) |
spec.json / 两个 README / 代码检查报告 / __tests__ / tsconfig.json |
交付方新增 |
第一次比 SHA256 时,index.js、auto.js、LICENSE 这几个哈希不一样 ,看起来像被改过。但把两边的行做 diff,一行都不差。真正的原因在字节层:
| 文件 | 本地 CR 计数 | npm 包 CR 计数 | 文件行数 |
|---|---|---|---|
index.js |
13 | 0 | 13 |
js/ios10Fix.js |
24 | 0 | 24 |
LICENSE |
21 | 0 | 21 |
差值恰好等于行数 ------是 git checkout 在 Windows 上把 LF 转成了 CRLF,不是内容改动。js/URL.js 是单行文件(19,998 字符一行),没有换行可转,所以它的哈希天然一致。
教训 :跨平台比对文件时,先排除换行符再下"被改过"的结论。SHA256 不一致只是"字节不同",不等于"内容不同";用"去掉 CR 后逐字符比较"才能确证。这次差点把 CRLF 误判成交付方改了上游代码。
交付仓库本身也是干净的(git status 无输出),TAG 4.0.0-ohos-1.0.0 就是唯一那个 commit。
3.1 交付包的工程质量
做得好的:
spec.json里upstream和upstreamCommit两样都记了 ,而且upstreamCommit与 npm 4.0.0 的gitHead完全对得上------可追溯性是完整的;spec.json明写implementation: pure JavaScript; no native module or HAR required,结论和实测一致;- 契约测试真的 import 了库并调用了方法(不是断言字面量那种怎么跑都会过的测试);
- README 主动声明了限制:"主机名不支持非 ASCII Unicode 规范化"------这条后来被实测复现了。
发现一处缺陷(如实记录,未改库代码):
交付包声明的
npm test在纯净检出下会直接失败。
package.json里写的是"test": "node --test __tests__/*.test.mjs",而测试文件 →js/URL.js→import {NativeModules} from 'react-native'。包里没有dependencies,react-native只出现在peerDependencies。纯净检出没有node_modules,Node 直接报:
Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'react-native' imported from ...\js\URL.js ✖ __tests__\url.test.mjs ℹ pass 0 ℹ fail 1也就是说:这份契约测试只在"宿主工程内"才跑得通。 我在 Node 侧放了一个只导出
NativeModules/Platform的 shim 之后测试才通过------说明失败原因是解析不到react-native,不是测试本身写错了。修复方向三选一:①
devDependencies里加react-native;② 测试前注入一个NativeModules桩;③ 让js/URL.js对NativeModules的读取失败降级(try/catch),使createObjectURL以外的功能不依赖它。
另有可补之处:交付包没有自带 WPT 语料 ,而上游 4.0.0 是自带的 (js/__tests__/wpt/)。它声明的契约测试是 2 个测试、约 10 条断言;上游自己那套是 1580 条。覆盖率差了几个量级------这个库恰恰是最适合用标准语料验的一类。
四、接入宿主:零原生接线,外加四处要注意的地方
纯 JS 库的接法比带原生实现的库简单得多,但有四处值得记。
1. 依赖用 file: 装
json
"react-native-url-polyfill": "file:../react-native-url-polyfill"
2. metro.config.js 的 watchFolders 必须加它的真实目录
file: 装进来是junction,不是真目录 ,Metro 只跟着 watchFolders 找源码。少了这一行,编译能过、但 bundle 里根本没有这个库:
js
watchFolders: [
// ...
path.resolve(__dirname, '../react-native-url-polyfill'),
],
3. 不要 动 harmony/entry/oh-package.json5
纯 JS 库不需要 HAR。这次刻意没碰它,而且专门确认了自动链接的计数没有增加:
info updated 4 file(s), linked 9 libraries, skipped 1 libraries
"linked 的计数不因纯 JS 库增加"是判断它真的不需要原生接线的信号。 如果计数涨了,说明要么库不是纯 JS,要么接线方式有问题。
另外,Metro 重定向清单(Redirected imports to N harmony-specific third-party package(s))里也不该出现纯 JS 库------那个清单是"鸿蒙变体重定向",纯 JS 库没有变体可重定向。
4. 本页额外加了一行 sourceExts
为了让 PC 端和设备端跑同一份测量代码 ,把测量模块写成了 .mjs。Metro 默认把 mjs 放在 additionalExts 而不是 sourceExts 里,所以显式加了一次:
js
resolver: {
sourceExts: ['js', 'jsx', 'json', 'ts', 'tsx', 'cjs', 'mjs'],
// ...
}
这一行只跟本次的测量方式有关,业务接入不需要。
五、构建与运行
bash
npm install
node_modules\.bin\react-native link-harmony # linked 9 libraries, skipped 1 libraries
cd harmony && ohpm install --all && cd ..
npm run dev # bundle
cd harmony && hvigorw --mode module -p product=default -p module=entry@default assembleHap --no-daemon
| 项 | 数值 |
|---|---|
首次 assembleHap |
6 分 34 秒 |
| 二次重编(改了一条断言) | 6 分 28 秒 |
| HAP 变化 | 80,867,224 → 81,260,441 字节 (+393,217 ≈ 384 KB) |
| bundle 产物 | bundle.harmony.js 6,248,387 字节 |
| Metro 重定向 | 清单里没有这个库(纯 JS,符合预期) |
| 原生注册日志 | 无 (它没有原生模块,不该有 TM created 这种日志) |
384 KB 的构成要拆开说 :库本身只有约 21 KB;剩下约 360 KB 是我为了让验证可信而打进 bundle 的 WPT 官方语料 (urltestdata.json 228 KB + setters_tests.json 82 KB + 豁免清单与 PC 基线约 9 KB)。业务接入不会带这些。别把测试语料的体积算到这个库头上。
这里还有个对比值得留意:这个库的编译耗时和之前那些带原生实现的库是同一个量级(6 分多) 。也就是说这 6 分钟几乎全是 RNOH 工程本身的 ABI / CMake 开销,跟接进来的是纯 JS 库还是原生库基本无关。加一个纯 JS 库省下的是"原生接线与调试"的时间,不是编译时间。
六、验证设计:URL 一致性怎么验才可信
URL 解析是确定性输出------同样的输入必须得到同样的输出。这类库不能只验"调用没报错",要上已知值断言。这次的三层设计如下。
6.1 第一层:找到标准语料,而不是自己编用例
react-native-url-polyfill 的上游 4.0.0 仓库自带 WPT 官方语料 (js/__tests__/wpt/)。WPT(Web Platform Tests)是浏览器 URL 实现共同遵守的一致性测试集,属于最高一档的"标准文档向量"------任何人都能拿去核对。语料构成:
| 文件 | 内容 | 计入用例 |
|---|---|---|
urltestdata.json |
1004 项(含字符串注释行),其中 891 项带输入:267 条期望失败 + 624 条期望成功 | 构造器用例 891 + origin 用例 411 = 1302 |
setters_tests.json |
顶层 11 个键,其中 comment 键是 20 行前言文本(不是用例),其余 10 个键共 278 条 |
278 |
| 合计 1580 |
用多少条自造用例都换不来"这 1580 条是标准"的分量。
6.2 第二层:把"上游已声明的偏差"和"未预期偏差"分开
这一层才是关键。 上游仓库里有一份 exceptions.json------上游自己承认"这些 WPT 用例我不合规",共 42 条。
如果没有这份清单,我跑出"42 条失败"就只能写"有 42 条不符合标准",读者没法判断严重程度。有了它,结论可以精确到:
上游 exceptions.json 声明 42 条已知偏差
├ 本次确实失败且已被上游声明:42
├ 未预期失败 :0
└ 声明了但本次通过(过时) :0
零未预期、零过时 ------意思是:这个实现的偏差恰好就是它自己声明的那些,不多不少。这比"通过率 97.3%"这种数字有力得多。
而且这个分类要求用例命名精确对齐 WPT 的测试名,所以我把测量代码按 WPT 的文件划分来组织:url-constructor 的字段(不含 origin)和 url-origin 的 origin 分成两个独立用例------否则一个因 href 失败的用例会"吃掉"它的 origin 失败,分类就错了。
6.3 第三层:独立 oracle ------ 但不能当基准
我用 Node 原生 WHATWG URL 跑了同一份语料。它是与这份 JS 完全不同的实现(V8 / Ada 原生),本意是"确认 WPT 的期望值本身可达成的"。
结果出了一个反转,值得单独讲。
七、PC 端与设备端跑同一份测量代码:跨引擎一致性
纯 JS 库在鸿蒙上最大的风险不是"跑不起来",而是"在 Hermes 上跑得跟别处不一样"。要证明这件事,光在设备上跑一遍不够------得有个对照。
做法是让 PC 和设备用同一个文件:
- 测量模块
wptUrlCorpus.mjs是纯 ESM、不依赖 React 也不依赖 RN; - PC :
node wpt/pc-run.mjs→ import 上游的js/URL.js跑语料(Node 需要能解析react-native,故加了一个只导出NativeModules/Platform的 shim,库文件保持逐字节不变); - 设备 :测试页
import {runUrlWpt} from './wptUrlCorpus.mjs',用同一份语料跑同一个实现。
两边结果:
| 实现 | 环境 | 用例总数 | 通过 | 失败 |
|---|---|---|---|---|
| 被测实现 | 鸿蒙 / Hermes | 1580 | 1538 | 42 |
| 同一份代码 | PC / Node v24.14.0 V8 | 1580 | 1538 | 42 |
| Node 原生 WHATWG URL | PC / V8 | 1580 | 1563 | 17 |
| 鸿蒙平台自带 URL | 鸿蒙 / Hermes | 1580 | 234 | 1346 |
通过数、失败数、失败集合全都一致,差集为 0 ------而且这个断言是在设备上直接算的(把 PC 端基线里的失败清单打进 bundle,设备端跑完做集合差)。
"测量代码同源"比"测量结果相同"更重要。 如果 PC 和设备各写一套测量代码,那"结果一致"可能只是两套代码碰巧都对,无法归因到被测实现。为此值得付出让 Metro 支持
.mjs的那一行配置。
耗时对照(同一份 1580 条语料):
| 引擎 | 耗时 |
|---|---|
| PC / V8 | 33 ms |
| 设备 / Hermes | 133 ms(约 4 倍) |
Hermes 比 V8 慢是预期内的(Hermes 优先启动速度与内存,不追求极致吞吐)。这里要说明的是:4 倍的差距没有带来任何行为差异------这才是鸿蒙侧真正要确认的事。
7.1 那个反转:不能拿原生实现当基准
Node 原生 URL 只过了 1563 / 1580 ,有 17 条不符合 WPT ;而这 17 条被测实现反而全部通过:
只有被测实现失败、原生通过的:42 条(= 该实现相对原生实现的能力缺口)
只有原生失败、被测实现通过的:17 条
Parsing: <http://a.b.c.xn--pokxncvks> without base
不应抛错:Invalid URL
Parsing: <http://10.0.0.xn--pokxncvks> without base
不应抛错:Invalid URL
Parsing: <http://a.b.c.XN--pokxncvks> without base
不应抛错:Invalid URL
...(6 条输入,各产生 constructor + origin 两条)
语料对这 6 条输入要求解析成功 (期望 href: http://a.b.c.xn--pokxncvks/),而 Node 原生直接抛 Invalid URL。node:url.domainToUnicode('xn--pokxncvks') 返回的串里带不可见字符,说明这类标签涉及不可见码点的映射------两边对规范的理解在这一角落上不一致,而按这份 WPT 快照,是原生这边不合规。
结论:如果我把"Node 原生 URL"当成唯一基准去做逐字段比对,我会把 17 条"实现比原生更合规"误判成缺陷。
所以基准的顺序必须是:
- 标准语料是基准(WPT);
- 原生实现只是参考实现之一 ,它可以帮你发现差异,但不能替标准下判决。
这条经验对所有"跨平台有原生对应物"的库都成立------平台自带的实现不一定是对的,甚至不一定比第三方库更接近标准。
八、同机对照:鸿蒙自带 URL 与 polyfill 差多少
上一节说明了"要跟标准比"。但还有一类问题只有同一台设备上的并排对照 能回答:鸿蒙平台自带的 URL 到底行不行?
平台自带 URL 是存在的:
鸿蒙自带 URL 是否存在 = true
鸿蒙自带 URLSearchParams 是否存在 = true
但用同一份语料一量:
| 实现 | 通过 / 总数 | 耗时 |
|---|---|---|
鸿蒙平台自带 URL |
234 / 1580 | 24 ms |
react-native-url-polyfill |
1538 / 1580 | 133 ms |
自带 URL 的首个失败样例就很说明问题:
用例:Parsing: <http://example\t.\norg> against <http://example.org/foo/bar>
期望:http://example.org/
实际:http://example.org/foo/bar/http://example\t.\norg
它把 base 当字符串拼接 ,根本没有相对 URL 解析------注意实际结果里,base 和 input 被直接连在了一起。这类"存在但残缺"的实现,比"完全不存在"更危险:typeof URL === 'function' 会让调用方以为可以放心用。
这也正面回答了这个库在鸿蒙上的价值:它不是"补一个缺失的东西",而是"把一个 234 分的实现换成 1538 分的实现"。 在端上拼 URL、解析 query、处理相对路径、做签名拼接这些场景里,这个差别是实打实的。
九、实测发现与已知限制
9.1 平台耦合点实测
| 观测项 | 实测值 |
|---|---|
Platform.OS |
harmony |
Platform.Version |
OpenHarmony-7.0.0.32(Beta2) |
| JS 引擎 | typeof HermesInternal === 'object' |
NativeModules.BlobModule 是否存在 |
true |
BlobModule.getConstants() 的键 |
BLOB_URI_HOST, BLOB_URI_SCHEME |
BLOB_URI_SCHEME / BLOB_URI_HOST |
blob / null |
URL.createObjectURL({data:{blobId:'demo',offset:0,size:3},size:3}) |
blob:demo?offset=0&size=3 |
URL.revokeObjectURL('blob:x') |
undefined(空实现,与上游一致) |
URL.canParse / URL.parse / URLSearchParams.prototype.size |
都存在 |
NativeModules.BlobModule 在鸿蒙上存在 ,所以 createObjectURL 走的是正常分支。我另外在 PC 端用不提供 BlobModule 的 shim 验证了另一条分支:那时 createObjectURL 会直接抛 Cannot create URL for blob!。
9.2 已声明的限制被实测复现:中文域名不转 punycode
上游为压体积把 URL 实现换成了"去掉 Unicode 映射表"的版本,代价是主机名不做非 ASCII Unicode 规范化。实测:
| 表达式 | 这个库给出 | Node 原生给出 |
|---|---|---|
new URL('https://百度.中国/').host |
百度.中国 |
xn--wkd006a.xn--fiqs8s |
new URL('https://例え.テスト/').host |
例え.テスト |
xn--r8jz45g.xn--zckzah |
new URL('https://Go.com/').host |
Go.com |
go.com |
这 42 条 WPT 偏差全部属于这一类原因(Unicode 主机名的 IDNA / UTS46 映射)。
业务上的实际影响 :如果用中文域名做请求,一定要自己先转 punycode------否则送出去的就是原始 Unicode 主机名,DNS 层解析不了。反过来,路径、查询值、片段里的非 ASCII 字符是正常百分号编码 的,不受这条限制影响(E 组验过:中文查询值 鸿蒙 编码为 %E9%B8%BF%E8%92%99,读回不变)。
9.3 已知限制清单
- 主机名不做非 ASCII Unicode 规范化(上游已声明,实测复现)------这是本库相对原生实现唯一的 42 条能力缺口,也是它换取体积的方式。
createObjectURL端到端未验证 :只确认了它返回blob:demo?offset=0&size=3这样的 uri,没有真的把 blob uri 喂给fetch/Image验证能取到内容。- 只在一台模拟器上验证 (
Pura X View,ohos-x64),没有真机。 js/ios10Fix.js的 iOS 10 分支永远不执行:鸿蒙上只验证了"这段模块级代码不抛错",无法验证它的功能正确性。- WPT 语料用的是上游仓库的快照 (
urltestdata.json+setters_tests.json),上游还有urlencoded-parser/idlharness等测试文件未纳入;hermes.json里上游也排除了一项(FormData 相关,与本库无关)。 - 未做真实的业务吞吐对比:只测了语料跑完耗时(Hermes 133 ms vs V8 33 ms)。
- 交付包缺陷 :
npm test在纯净检出下失败(解析不到react-native),见 3.1 节。与设备运行无关,未改库代码。
未发现实现层面的缺陷。 42 条偏差不是缺陷,是上游公开声明并写进 README 的取舍。
十、常见问题
Q1:这个库需要 HAR、权限或 ohpm 接线吗?
都不需要。它没有原生模块,harmony/entry/oh-package.json5 不用动,module.json5 也不用加权限。判断信号是:link-harmony 的 linked 计数不因它增加 (本次输出 linked 9 libraries, skipped 1 libraries)。
Q2:react-native-url-polyfill 和 react-native-url-polyfill/auto 有什么区别?
auto入口 :import 'react-native-url-polyfill/auto'→ 在Platform.OS !== 'web'时自动调用setupURLPolyfill(),把实现装到全局globalThis.URL/URLSearchParams。适合"想让全项目透明用上标准 URL"。- 库入口 :
import {URL} from 'react-native-url-polyfill'→ 只导出实现,不动全局(ponyfill 用法)。适合"只想在局部用,不想改变全局行为"。
两种可以共存;setupURLPolyfill() 也可以手动调用。
Q3:装了之后怎么确认装上了?
调用 setupURLPolyfill() 会留一个版本标记:
js
import {setupURLPolyfill} from 'react-native-url-polyfill';
setupURLPolyfill();
console.log(globalThis.REACT_NATIVE_URL_POLYFILL); // react-native-url-polyfill@4.0.0
Q4:平台自带的 URL 不是已经能用了?
能用,但只过 234 / 1580 条标准用例。最典型的问题是不支持相对 URL 解析:
js
new URL('/search?q=1', 'https://example.com/base')
// 平台自带:把 base 当字符串拼在后面 → https://example.com/base/search?q=1 ✗
// 这个库 :https://example.com/search?q=1 ✓
Q5:中文域名为什么没有换成 punycode?
上游为压体积去掉了 Unicode 映射表,这是它公开声明的限制。需要中文域名时,请求前自己转一次:
js
// 先转 punycode 再交给 URL
const host = punycode.toASCII('百度.中国'); // xn--wkd006a.xn--fiqs8s
路径、查询值、片段里的中文不受影响,会正常做百分号编码。
Q6:既然有 42 条不符合 WPT,能不能算"适配不通过"?
不算。这 42 条正好等于上游 exceptions.json 自己声明的 42 条,零未预期、零过时------也就是说这个实现的偏差范围是被作者完整掌握并公开的,而且同类原因(Unicode 主机名)在 README 里也写了。
真正需要警惕的是"未预期偏差":如果跑出 43 条,那一条才是要查的。
Q7:URL.createObjectURL 在鸿蒙上能用吗?
它依赖 NativeModules.BlobModule。实测鸿蒙上 BlobModule 存在 (BLOB_URI_SCHEME = blob),所以能返回 blob: 开头的 uri。但我没有做端到端验证(没真的用这个 uri 去取 Blob 内容),涉及 Blob 下载的场景建议自己补一轮。
Q8:这个库会跟别的库抢全局 URL 吗?
用 auto 入口或手动 setupURLPolyfill() 时,它会覆盖全局 URL / URLSearchParams。如果项目里还有别的库也改全局 URL,就有顺序问题------这时建议改用库入口(ponyfill),只在需要的地方 import。
Q9:怎么自己把这一轮验证跑一遍?
bash
# PC 端复算(同一份测量代码)
node wpt/pc-run.mjs
# 设备端
hdc shell aa force-stop com.rnoh084.demo
hdc shell aa start -b com.rnoh084.demo -a EntryAbility --ps rnAppKey UrlPolyfillTestApp
# 界面上点「跑全部」
hdc shell "hilog -x | grep urlpolyfill-test" | Select-String '汇总|✗'
小结
react-native-url-polyfill 是个不需要原生适配的库:没有 harmony/ 目录,实现文件与 npm 上游逐字节一致 ,接进宿主只需要改 package.json、metro.config.js、index.js 三处,自动链接的计数都不会增加。
但这次真正花时间的不是"接进来",而是"证明它是对的"。几点体会:
-
"不需要适配"要证明,而且证明的落点是"对不对",不是"能不能跑"。 代码量小、接口简单,都不等于验证简单。
-
找标准语料,别自己编用例。 WPT 那 1580 条是浏览器共同遵守的一致性测试集,用它是把"我觉得对"换成"标准认为对"。而且上游自己带了这份语料,先翻仓库再动手写测试。
-
"上游自己声明的偏差"是一份礼物。 有了
exceptions.json,42 条失败才能被精确切成"已知 42 / 未预期 0";没有它,这个数字既没法解释也不可信。遇到带豁免清单的交付包或上游,一定要拿来用。 -
不能拿原生实现当基准。 Node 原生 URL 有 17 条不符合 WPT,反倒被测实现全过。用原生做 oracle 会把"更合规"误判成"有缺陷"。基准只能是标准;原生只是参考实现之一。
-
测量代码同源,比测量结果相同更重要。 PC 与设备共用同一个
.mjs,"结果一致"才能归因到被测实现,而不是"两套测量代码碰巧都对"。 -
"存在但残缺"比"不存在"更危险。 鸿蒙自带
URL是存在的、typeof是function------只断言"存在"就通过了,实际只过 234/1580。同机对照能一刀切开这种假象,而这台设备上唯一能跟它对量的,就是同一份标准语料。
关于交付包:它的可追溯性做得不错 (upstream 与 upstreamCommit 都在,且与 npm 的 gitHead 对得上),契约测试也是真的在调用库 。不足有两点:npm test 在纯净检出下会失败 (解析不到 react-native),以及没有把上游自带的 WPT 语料收进来------对这类"有标准可依"的库,这是最值得补的一块。


本篇用到的库
| 项 | 内容 |
|---|---|
| 三方库 | react-native-volume-control(上游 1.0.1 的鸿蒙适配版) |
| 适配仓库 | https://atomgit.com/oh-react-native/react-native-volume-control |
| 适配 TAG | 1.0.1-ohos-1.0.0 |
| ohpm 包名 | @react-native-ohos/react-native-volume-control |
| HAR | harmony/volume_control.har(3,756 字节) |
| 原生模块名 | VolumeControl(ArkTS TurboModule,日志标签 #RNOH_ARK) |
| 是否需要权限 | 不需要(媒体音量读写不要求声明权限) |
| 上游仓库 | https://github.com/rtmalone/react-native-volume-control(基线 commit 2915b27068fe67c7a835da2f136fa58d9b7c6cae;npm 元数据声明 ISC,随包 LICENSE 为 MIT) |
| 宿主工程 | RNOH084Demo(测试页 rnAppKey = VolumeControlTestApp) |
接入方式(纯 JS,零原生接线):
json
// package.json
"react-native-url-polyfill": "file:../react-native-url-polyfill"
js
// metro.config.js ------ file: 装进来是 junction,必须让 Metro 找得到源码
watchFolders: [
path.resolve(__dirname, '../react-native-url-polyfill'),
],
ts
// 用法一:ponyfill,不动全局
import {URL, URLSearchParams} from 'react-native-url-polyfill';
// 用法二:装到全局(auto 入口)
import 'react-native-url-polyfill/auto';
// 用法三:手动装到全局
import {setupURLPolyfill} from 'react-native-url-polyfill';
setupURLPolyfill();
console.log(globalThis.REACT_NATIVE_URL_POLYFILL); // react-native-url-polyfill@4.0.0
// 典型用法:相对 URL + 中文查询值
const u = new URL('/search?q=鸿蒙', 'https://example.com/base');
u.toString(); // https://example.com/search?q=%E9%B8%BF%E8%92%99
u.origin; // https://example.com
u.searchParams.get('q'); // 鸿蒙
u.searchParams.append('page', '2');
// 安全解析用户输入
URL.canParse('https://example.com'); // true
URL.canParse('not a URL'); // false
bash
# 换页启动测试页(force-stop 不能省,换页参数只在冷启动生效)
hdc shell aa force-stop com.rnoh084.demo
hdc shell aa start -b com.rnoh084.demo -a EntryAbility --ps rnAppKey UrlPolyfillTestApp
验证环境
| 项 | 版本 |
|---|---|
| React Native | 0.84.1 |
| React | 19.2.3 |
| RNOH(npm / ohpm) | @react-native-oh/react-native-harmony / @rnoh/react-native-openharmony 0.84.3 |
| Node.js | v24.14.0(PC 端复算用) |
| DevEco Studio | 26.0.0.621 |
| HarmonyOS SDK | API 26(26.0.0.32) |
| 设备 | HarmonyOS 7.0.0(26.0.0) Beta2 模拟器 Pura X View(ohos-x64),Platform.Version 报 OpenHarmony-7.0.0.32(Beta2) |
| JS 引擎 | Hermes |
| 宿主 HAP 产物 | entry-default-signed.hap(81.26 MB) |
| 本次增量构建 | assembleHap 6 分 34 秒,HAP +393,217 字节(≈384 KB,其中约 360 KB 是测试语料) |
| 验证规模 | 设备侧 35 / 35 断言全部通过 ;WPT 官方语料 1580 条:通过 1538,失败 42(全部为上游声明项) ;与 PC 端失败集合差集为 0 |
欢迎加入 CPF-RN 鸿蒙社区:https://atomgit.com/CPF-RN
React Native for OpenHarmony 组织:https://atomgit.com/oh-react-native
RN 三方库鸿蒙适配清单:https://atomgit.com/oh-react-native/rn-ohos-adaptation-overview