React Native for OpenHarmony 实战:三方库 react-native-hyperlink 的鸿蒙化适配指南
本文记录把 react-native-hyperlink(把文本里的 URL / 邮箱 / 裸域名变成可点链接的组件)适配到 HarmonyOS 的完整过程。
它的工作方式很"框架无关":不自己画链接,而是把一段文本切分成"普通文字 + 链接"若干段,每段链接都渲染成一个可点的 <Text>,并把 URL 与显示文本通过回调交给你。
这一轮有两个值得写下来的结果:一个依赖版本不兼容的坑,以及链接化结果可以被完整核对。
★★ 一、坑:装错一个次版本,整个应用起不来
库源码里是这么写的一行:
// src/Hyperlink.tsx L10
const linkify = require('linkify-it')();
而它声明的依赖是 linkify-it: ^5.0.0。我按"缺什么装什么"的思路直接:
npm install linkify-it mdurl --ignore-scripts # ✗ 没写版本
装到的是 linkify-it@6.1.0,于是启动后整个页面白屏,dump 出来是:
[runtime not ready]: TypeError: Object is not a function
anonymous (bundle.harmony.js:205925:61) → loadModuleImplementation → guardedLoadModule → …
"HyperlinkTestApp" has not been registered. …
根因(对比两个版本的 CJS 入口结尾):
// linkify-it@6.1.0 build/index.cjs.js
exports.LinkifyIt = LinkifyIt;
exports.REBuilder = REBuilder;
exports.linkifyit = linkifyit; // ⇒ 导出的是【对象】
// linkify-it@5.0.2 build/index.cjs.js
module.exports = LinkifyIt; // ⇒ 导出的是【函数】
⇒ require('linkify-it')() 在 v5 下正常,在 v6 下必然抛 Object is not a function ✓
改法:按库声明的版本范围装:
npm install "linkify-it@^5.0.0" "mdurl@^2.0.0" --ignore-scripts # ✓
⇒ 重装后 linkify-it 变 5.0.2,打包/构建/启动全部正常 ✓✓
为什么容易踩:用
file:目录方式接入时,npm 不会安装那个库声明在dependencies里的依赖,得自己补 —— 而一旦补装时不带版本,就会装到最新大版本,于是"能用 v5 的代码"遇上"改成具名导出的 v6"就炸了。
★★ 二、链接化结果可以被完整核对
组件本身不画东西,所以判据放在"它切出了什么"上。我用两个可观测钩子:
<Hyperlink
linkStyle={{color: '#D0021B'}} // 三者至少给一个,才会做链接化
linkText="[LINK]" // 把每个链接的显示文本统一替换掉
onPress={(url, text) => log(url, text)} // 点击时拿到 url 与显示文本
injectViewProps={url => { log(url); return {}; }} // ★ 每个链接渲染时都会调用一次
>
<Text>官网 https://atomgit.com/CPF-RN 邮箱 hi@example.com 裸域名 example.org 结束</Text>
</Hyperlink>
实测(四个实例,只有第一个/第三个/第四个给了 handler 或 style):
★ 每个实例识别到的链接数 = H1=3 H2=0 H3=3 H4=3
★ 识别到的 URL 种类(去重) = https://atomgit.com/CPF-RN | mailto:hi@example.com | http://example.org
⇒ 三种链接都被正确识别,而且做了规范化:
| 原文 | 识别出的 URL |
|---|---|
https://atomgit.com/CPF-RN | https://atomgit.com/CPF-RN(原样) |
hi@example.com | mailto:hi@example.com(补了 mailto:) |
example.org | http://example.org(裸域名补了 http://) |
⇒ 而 H2 是 0 —— 它没有给 onPress/onLongPress/linkStyle,于是走了源码里的"原样渲染"分支 ✓✓ 这是一个天然对照组。
切分结果还能直接从布局 dump 里读出来:
Text#Paragraph(114) "官网 [LINK] 邮箱 [LINK] 裸域名 [LINK] 结束"
⇒ 三个链接都变成了 [LINK],而非链接文字(官网 / 邮箱 / 裸域名 / 结束)完整保留 ✓
点击也是对的:
CLICK url=https://atomgit.com/CPF-RN text=[LINK]
⇒ onPress(url, text) 两个参数都符合预期 ✓
设备侧断言 22 / 22 全部通过。

一、先说结论
| 项 | 结果 |
|---|---|
| 上游最新版 | 0.1.2;license: MIT;上游 star 811;pushed 2026-02-19(很活跃) |
| 库类型 | 纯 JS 组件库(TypeScript 源码 + bob 构建产物) |
| 是否需要原生适配 | 不需要(无 harmony/、无 NativeModules) |
| 运行时依赖 | 两个:linkify-it、mdurl(宿主原本都没有) |
⚠️ prepare: "bob build" | ⇒ 安装必须 --ignore-scripts |
| 交付包形态 | 同时带 src/(TS 源码)与 lib/(编译产物) ⇒ 实现可读 ✓ |
| 需要 HAR / 权限 / ohpm | ❌ 都不需要 |
| 设备侧断言 | ✅ 22 / 22 通过(A 9 + B 11 + C 2) |
| ★★ 依赖版本坑 | npm install linkify-it(不带版本)装到 6.1.0 ⇒ 启动 TypeError: Object is not a function;按 ^5 装(5.0.2)即好 |
| ★ 老 API 预扫 | ✅ 一个都没有(没有 ViewPropTypes/contextTypes/componentWillReceiveProps/ListView) |
| ★ 成员写法 | 原型方法 constructor/render/handleLink;箭头类字段 linkify/parse(在未导出的内层类上) |
| ★★ 导出形态 | 命名导出 Hyperlink 与默认导出是同一个类;真正实现的内层类没有被导出 ⇒ 它的 defaultProps / getDerivedStateFromProps 外部不可达 |
| ★★ 链接化实测 | H1/H3/H4 各 3 个链接;H2 = 0(不给 handler/style ⇒ 不切分) |
| ★★ URL 规范化 | 邮箱 ⇒ mailto:…;裸域名 ⇒ http://… |
| ★ 点击 | onPress(url, text) 实测拿到 https://atomgit.com/CPF-RN 与 [LINK] |
二、判定与接入
| 步 | 做法 | 结果 |
|---|---|---|
| ① | package.json 里有没有 harmony.autolinking | 没有 |
| ② | 仓库里有没有 harmony/ | 没有 |
| ③ | 代码里有没有 NativeModules | 全无 |
| ④ | import 图体检 | linkify-it + mdurl + react/react-native ⇒ 两个真依赖,宿主都没有 |
| ⑤ | scripts 扫 prepare 家族 | ⚠️ 有 prepare: "bob build" ⇒ 必须 --ignore-scripts |
| ⑥ | ★ 老 API 预扫 | ✅ 干净(这个库较新,TS + bob) |
| ⑦ | ★ 依赖版本预查 | ⚠️ 必须按库声明的版本范围装(见第一节的坑) |
npm install "git+https://atomgit.com/oh-react-native/react-native-hyperlink.git#0.1.2-ohos-1.0.0" --ignore-scripts
# ★ 必须补装运行时依赖,而且【按库声明的版本范围】装
npm install "linkify-it@^5.0.0" "mdurl@^2.0.0" --ignore-scripts
依赖坑的实测(先不装,直接打包):
error Unable to resolve module linkify-it from E:\rnoh-work\react-native-hyperlink\lib\module\Hyperlink.js:
linkify-it could not be found within the project or in these directories…
装完后打包 exit 0 ✓;但装错版本会在运行期炸(见第一节)。
// metro.config.js
watchFolders: [ path.resolve(__dirname, '../react-native-hyperlink') ],
import HyperlinkTestApp from './HyperlinkTestApp';
AppRegistry.registerComponent('HyperlinkTestApp', () => HyperlinkTestApp);
hdc shell aa force-stop com.rnoh084.demo
hdc shell aa start -b com.rnoh084.demo -a EntryAbility --ps rnAppKey HyperlinkTestApp
不需要 HAR、权限、ohpm、link-harmony。
三、读源码取到的真值
交付包同时带
src/Hyperlink.tsx(6736 B)与lib/module/Hyperlink.js(编译产物),所以实现完全可读。
3.1 ★★ 两个类,但只导出一个
// src/Hyperlink.tsx
class Hyperlink extends Component<HyperlinkProps, HyperlinkState> { // ① 真正实现(L14)
…
}
export default class extends Component<HyperlinkProps> { // ② 薄包装(L198,被导出)
handleLink(url) {
const urlObject = parse(url);
urlObject.protocol = urlObject.protocol.toLowerCase();
const normalizedURL = format(urlObject);
Linking.canOpenURL(normalizedURL).then(supported => supported && Linking.openURL(normalizedURL));
}
render() {
const onPress = this.props.onPress ?? this.handleLink;
return this.props.linkDefault
? <Hyperlink {...rest} onPress={onPress} />
: <Hyperlink {...rest} />;
}
}
// src/index.tsx
export { default as Hyperlink } from './Hyperlink';
import Hyperlink from './Hyperlink';
export default Hyperlink;
实测:
命名空间导出(Object.keys) = Hyperlink, default
★★ 具名导出 Hyperlink 与默认导出【是同一个类】 = true
导出类的原型方法命中数(constructor/render/handleLink ⇒ 3) = 3
2 个箭头类字段(linkify/parse)在导出类 prototype 上的个数 = 0
★ 导出类上没有 isTextNested(内层类原型方法,外部不可见) = undefined
★★ 导出类的 defaultProps = undefined
★★ 导出类的 getDerivedStateFromProps 的 typeof = undefined
⇒ src/index.tsx 里两个导出指向同一个类(都是那个薄包装类);
⇒ 真正实现的内层类没有被导出 ⇒ 它身上的 defaultProps(linkify + injectViewProps)与 getDerivedStateFromProps 从外部取不到 ✓
⇒ linkify / parse 是内层类的箭头类字段(linkify = component => {…}、parse = component => {…}),不在 prototype 上,外部也看不到 ✓
3.2 ★★ 关键分支:三者都不给就不做链接化
render() {
const { key: _key, ref: _ref, ...viewProps } = this.props as any;
// If no link handlers or styles, just render children as-is
if (!this.props.onPress && !this.props.onLongPress && !this.props.linkStyle) {
return <View {...viewProps} style={this.props.style}>{this.props.children}</View>;
}
…
}
⇒ onPress / onLongPress / linkStyle 至少要有一个,才会去解析并链接化 ✓
⇒ 这正是本轮 H2 = 0 的原因 ✓(天然对照组)
3.3 解析与切分
linkify = (component) => {
if (typeof component.props.children !== 'string') return component;
const childrenString = component.props.children;
if (!this.state.linkifyIt.pretest(childrenString) || !this.state.linkifyIt.test(childrenString)) return component;
let elements = []; let _lastIndex = 0;
try {
this.state.linkifyIt.match(childrenString)?.forEach(({index, lastIndex, text, url}) => {
let nonLinkedText = childrenString.substring(_lastIndex, index);
nonLinkedText && elements.push(nonLinkedText); // ★ 非链接文字原样保留
_lastIndex = lastIndex;
if (this.props.linkText) text = typeof linkText === 'function' ? linkText(url) : linkText;
…
elements.push(<Text … onPress={() => onPress?.(url, text)} …>{text}</Text>);
});
elements.push(childrenString.substring(_lastIndex, childrenString.length)); // ★ 补上尾巴
return React.cloneElement(component, componentProps, elements);
} catch (err) { return component; } // ★ 出错就静默降级(原样返回)
};
- ★
linkText可以是字符串或函数(url => string)⇒ 我实测用"[LINK]"与函数两种都生效 ✓ - ★ 非链接文字按
substring(_lastIndex, index)切出来并保留 ✓(dump 里能看到官网/邮箱/裸域名/结束) - ★
try/catch包住 ⇒ 出错静默降级(原样返回,不抛给使用者)
3.4 其它
isTextNested(component) {
if (!React.isValidElement(component)) throw new Error('Invalid component');
let { type: { displayName } = {} } = component;
if (displayName !== 'Text') throw new Error('Not a Text component');
return typeof component.props.children !== 'string';
}
⇒ 这是会抛错的(Invalid component / Not a Text component)—— 本轮不故意触发(见第七节)。
public static getDerivedStateFromProps(nextProps, prevState) {
return nextProps.linkify !== prevState.linkifyIt ? { linkifyIt: nextProps.linkify || linkify } : null;
}
⇒ 传 linkify prop 可以换成自己的实例(受控);同引用时返回 null ✓
四、验证设计
| 判据 | 内容 |
|---|---|
| ① 结构 | 导出形态(具名 vs 默认是否同一个类)、原型方法 vs 箭头类字段(先看源码写法再断言) |
| ② 链接化(定量) | 每个实例被识别出来的链接数(用 injectViewProps 计数)⇒ 含 H2=0 对照组 |
| ③ 规范化 | 邮箱是否补 mailto:、裸域名是否补 http:// |
| ④ 切分(读文本) | 从布局 dump 读 [LINK] 与保留的非链接文字 |
| ⑤ 点击(交互) | 点一个链接 ⇒ onPress(url, text) 的两个参数 |
五、实测结果
5.1 断言 22 / 22
| 组 | 内容 | 结果 |
|---|---|---|
| A 契约与结构 | 默认/具名导出同一性、导出类的 3 个原型方法、linkify/parse 不在 prototype、isTextNested 不可见、defaultProps/getDerivedStateFromProps 外部不可达 | 9/9 |
| B 渲染与链接化 | 四个实例渲染未抛错、标题安全区、每实例链接数 H1=3 H2=0 H3=3 H4=3、三种 URL 规范化 | 11/11 |
| C 交互与边界 | 点的链接拿到正确的 url/text、边界记录 | 2/2 |
| 合计 | 22/22 |
关键观测值:
命名空间导出(Object.keys) = Hyperlink, default
导出类的原型方法命中数(constructor/render/handleLink ⇒ 3) = 3
2 个箭头类字段(linkify/parse)在导出类 prototype 上的个数 = 0
导出类的 defaultProps = undefined 导出类的 getDerivedStateFromProps 的 typeof = undefined
★ 每个实例识别到的链接数 = H1=3 H2=0 H3=3 H4=3
★ 识别到的 URL 种类(去重) = https://atomgit.com/CPF-RN | mailto:hi@example.com | http://example.org
CLICK url=https://atomgit.com/CPF-RN text=[LINK]
UI 安全区:TITLE measureInWindow y=420 > 117 ✓
5.2 切分结果(读 dump)
Text#Paragraph(114) [84,1596,1236,1662] "官网 [LINK] 邮箱 [LINK] 裸域名 [LINK] 结束"
⇒ 三个链接被替换成 [LINK](linkText="[LINK]"),四段非链接文字完整保留 ✓
5.3 点击
CLICK url=https://atomgit.com/CPF-RN text=[LINK]
⇒ 点第一个 [LINK] 得到的就是第一个链接的 URL,且 text 是替换后的显示文本 ✓



六、本轮踩的两个"工程"坑
6.1 ★★ 装依赖漏了版本(第一节已详述)
npm install linkify-it ⇒ 6.1.0 ⇒ require('linkify-it')() 抛 Object is not a function ⇒ 应用起不来。
⇒ 按库声明的版本范围装(linkify-it@^5.0.0)⇒ 5.0.2 正常。
⇒ 判断依据:看库 package.json 的 dependencies,别凭"最新版应该更兼容"的直觉。
6.2 ★★ 构建失败后直接重试会一直失败,要先 clean
我改完测试页后 hvigorw assembleHap 报了 BUILD FAILED(只用了 56 秒),再重试一次仍然失败(47 秒),错误是:
Cannot find name 'px2vp'.
关键:这些 Cannot find name 'px2vp' 是 oh_modules 里框架代码的告警,在成功构建里也会出现 ⇒ 不是真因。
真正的问题是上一次失败把增量缓存弄坏了 ⇒ hvigorw clean 之后再 assembleHap:
Finished :entry:clean... after 41 s
BUILD SUCCESSFUL in 15 min 59 s 398 ms
⇒ 修好了(HAP 时间戳变成新的),但 clean + 全量重建花了约 16 分钟 ⇒ 值得在流程里避免"连续重试失败的构建"。
6.3 ★ 差点被"陈旧产物"骗过(老坑重现)
npm run dev(打包)exit=0,但同一次作业里 hvigorw 是 BUILD FAILED ⇒ 我安装的是上一次的旧 HAP ⇒ 页面上跑的还是旧断言(打印出一堆早已改掉的失败项)。
⇒ 教训:必须分别检查打包与构建的退出码;本次是因为我看了 bundle exit=0 就继续了,没看 hvigor 的退出码。
七、已知限制与本次验证边界
7.1 用这个库要知道的
| 事项 | 说明 |
|---|---|
必须补装 linkify-it + mdurl,且按版本范围装 | linkify-it@^5、mdurl@^2 |
prepare: "bob build" | 安装要 --ignore-scripts |
| 三者至少给一个才会链接化 | onPress / onLongPress / linkStyle |
linkText 可以是字符串或函数 | 函数签名是 url => string |
| 邮箱/裸域名会被规范化 | 邮箱 ⇒ mailto:,裸域名 ⇒ http:// |
injectViewProps(url) 每个链接调用一次 | 是很好的埋点入口 |
linkDefault 为真时缺省走 Linking | 会真的去 canOpenURL / openURL |
linkify prop 可换实例 | 由 getDerivedStateFromProps 同步 |
defaultProps/getDerivedStateFromProps 外部取不到 | 它们在未导出的内层类上 |
isTextNested 会抛错 | Invalid component / Not a Text component |
7.2 本次验证的边界
linkDefault的真实跳转未验证(会尝试Linking.openURL跳出应用,本轮故意不触发);onLongPress未实测(H4 只用来验证"它也算三者之一 ⇒ 会切分");linkifyprop 换成自定义实例未验证(只从源码看getDerivedStateFromProps的同步逻辑);isTextNested的抛错未复现(故意不触发 —— 被捕获的 TypeError 在 RNOH 上仍会整屏红屏);- 嵌套
<Text>/ 深层 children 的递归parse未逐层验证(只验证了单层<Text>与三处链接); Link的样式只验证了"能生效"(红色[LINK]可见),未逐像素核对linkStyle;style透传、key/ref过滤等边界未逐项验证;@types/*两个依赖只在编译期用,运行期未验证(本工程未做类型检查构建);- 只在一台模拟器上验证(
Pura X View,density 3,1320×2232),没有真机;未做跨平台对照。
未改动库代码。
八、常见问题
Q1:装完启动就白屏,报 TypeError: Object is not a function?
多半是 linkify-it 装到了 6.x。库代码是 require('linkify-it')(),而:
// v6:具名导出(对象) exports.LinkifyIt = LinkifyIt; …
// v5:module.exports = LinkifyIt (函数)
⇒ 按库声明装:npm install "linkify-it@^5.0.0" --ignore-scripts。
Q2:为什么我的链接没被识别?
检查这三者是不是至少给了一个:onPress、onLongPress、linkStyle。源码里三者全无时会原样渲染 children(实测这样的实例识别到 0 个链接)。
Q3:怎么知道它识别出了哪些链接?
用 injectViewProps:
<Hyperlink injectViewProps={url => { console.log(url); return {}; }} … />
它每个链接渲染时都会调用一次 ⇒ 直接拿到 URL 列表。
Q4:邮箱和裸域名会怎么处理?
会被规范化:
| 原文 | 识别结果 |
|---|---|
hi@example.com | mailto:hi@example.com |
example.org | http://example.org |
Q5:想把链接显示成别的文字?
<Hyperlink linkText="[链接]" … /> // 固定文本
<Hyperlink linkText={url => `[${new URL(url).host}]`} … /> // 用函数
Q6:点击回调的参数是什么?
onPress(url, text) —— url 是规范化后的 URL,text 是当前显示文本(如果你用 linkText 换过,这里就是换过之后的)。
实测:CLICK url=https://atomgit.com/CPF-RN text=[LINK]。
Q7:不传 onPress 会怎样?
linkDefault 为真时会走内置的 handleLink:Linking.canOpenURL(normalizedURL).then(ok => ok && Linking.openURL(normalizedURL)) —— 也就是真的去打开这个 URL。
Q8:这个库需要鸿蒙侧做什么特殊处理吗?
不需要原生侧处理(纯 JS)。要留意的是:依赖要按版本范围补装,以及 prepare 脚本要求 --ignore-scripts。
小结
react-native-hyperlink 是纯 JS 的文本链接化组件(TS 源码 + bob 产物,零原生依赖)。设备侧 22 / 22 断言通过。
最值得记下来的是这几条:
-
★★ 依赖必须"按库声明的版本范围"补装,不能图省事装最新版。
npm install linkify-it⇒ 6.1.0 ⇒ 库的require('linkify-it')()抛TypeError: Object is not a function⇒ 整个应用起不来(has not been registered)。
根因是两个版本的 CJS 导出形态不同:v5 是module.exports = LinkifyIt(函数),v6 改成了exports.LinkifyIt = …(具名,对象)。
⇒ 按^5.0.0装(5.0.2)即好 ✓ -
★★ "它切出了什么"是可以被完整核对的。 用
injectViewProps数链接、用 dump 读切分后的文本、用onPress(url, text)核对点击:- 每实例链接数
H1=3 H2=0 H3=3 H4=3⇒ H2 是天然对照组(不给onPress/onLongPress/linkStyle⇒ 源码里直接"原样渲染"); - URL 规范化:
hi@example.com → mailto:hi@example.com、example.org → http://example.org; - 切分文本:
"官网 [LINK] 邮箱 [LINK] 裸域名 [LINK] 结束"⇒ 链接被替换、非链接文字保留; - 点击:
CLICK url=https://atomgit.com/CPF-RN text=[LINK]。
- 每实例链接数
-
★★ 导出的类不是实现类。
src/index.tsx里具名导出与默认导出指向同一个薄包装类,真正实现的内层类没有被导出 ⇒ 它的defaultProps、getDerivedStateFromProps、isTextNested从外部都取不到(实测全是undefined)。⇒ 写断言前先确认"导出的是哪一个类"。 -
★★ 构建失败后要先
clean,不要连着重试。 我遇到BUILD FAILED(56 秒)后直接重试,仍然失败(47 秒),报的是Cannot find name 'px2vp'—— 那是oh_modules框架代码的告警,成功构建里也有。hvigorw clean后再构建即成功(但花了约 16 分钟全量重建)。 -
★ 分别检查"打包"与"构建"的退出码(老坑重现)。本次一次作业里
npm run dev是exit=0,而hvigorw是BUILD FAILED⇒ 我装的是旧 HAP,页面上跑的是一堆早已改掉的旧断言。 -
★ 预扫正则要能匹配 TAB 缩进。 我第一次扫"成员写法"时用了
^\s{2,4},而该文件用 TAB 缩进(一个 TAB 只算 1 个空白字符)⇒ 漏掉了linkify/parse两个箭头类字段。⇒ 用^\s*或^\s+更稳。
本篇用到的库
| 项 | 内容 |
|---|---|
| 三方库 | react-native-hyperlink(上游 0.1.2 的鸿蒙适配版) |
| 适配仓库 | https://atomgit.com/oh-react-native/react-native-hyperlink |
| 适配 TAG | 0.1.2-ohos-1.0.0 |
| 需要 HAR / 权限 / ohpm | 都不需要(纯 JS) |
| 库类型 | 纯 JS 组件库:把文本里的 URL / 邮箱 / 裸域名变成可点链接 |
| 运行时依赖 | linkify-it@^5.0.0 + mdurl@^2.0.0(⚠️ 版本不能随便升) |
prepare / postinstall | ⚠️ 有 prepare: "bob build" ⇒ 安装用 --ignore-scripts |
| 许可证 | MIT |
| 上游仓库 | https://github.com/obipawan/react-native-hyperlink(★811) |
| 宿主工程 | RNOH084Demo(测试页 rnAppKey = HyperlinkTestApp) |
# 从适配仓库安装
npm install "git+https://atomgit.com/oh-react-native/react-native-hyperlink.git#0.1.2-ohos-1.0.0" --ignore-scripts
# ★ 必须补装运行时依赖,并且【按库声明的版本范围】装
npm install "linkify-it@^5.0.0" "mdurl@^2.0.0" --ignore-scripts
// metro.config.js
watchFolders: [ path.resolve(__dirname, '../react-native-hyperlink') ],
import Hyperlink from 'react-native-hyperlink';
// ① 基本用法:三者至少给一个,才会做链接化
<Hyperlink
linkStyle={{color: '#D0021B'}} // ← 给样式
onPress={(url, text) => console.log(url, text)} // ← 或给回调
linkText="[链接]" // 替换显示文本(也可传 url => string)
injectViewProps={url => { console.log(url); return {}; }} // ★ 每个链接调用一次
style={{padding: 8}}
>
<Text>官网 https://atomgit.com/CPF-RN 邮箱 hi@example.com 裸域名 example.org 结束</Text>
</Hyperlink>
// ② 缺省打开(不传 onPress 时用内置的 Linking)
<Hyperlink linkDefault linkStyle={{color: 'blue'}}>
<Text>点我 https://example.com</Text>
</Hyperlink>
// ③ 换掉内置的 linkify 实例
<Hyperlink linkify={myLinkifyInstance} linkStyle={{color: 'blue'}}>…</Hyperlink>
// ⚠️ onPress / onLongPress / linkStyle 三者全不给 ⇒ 原样渲染,【不】做链接化
// ⚠️ 邮箱会被规范化成 mailto:…,裸域名会被规范化成 http://…
// ⚠️ 依赖版本:linkify-it 必须是 5.x(6.x 改成具名导出 ⇒ require(...)() 会抛 TypeError)
# 换页启动测试页 —— force-stop 不能省
hdc shell aa force-stop com.rnoh084.demo
hdc shell aa start -b com.rnoh084.demo -a EntryAbility --ps rnAppKey HyperlinkTestApp
判据的复现方式
# ① 结构:先看源码写法(⚠️ 用 ^\s* 兼容 TAB 缩进),再断言"分组"
# 原型方法:^\s*(\w+)\s*\([^)]*\)\s*\{
# 箭头类字段:^\s*(\w+)\s*=\s*(\(|function|async)
# ② 链接化数量:用 injectViewProps 埋点,每个链接调用一次
# ⇒ 给每个实例不同的 tag(H1|url、H2|url…),就能分别计数并做对照
# ③ 切分文本:从布局 dump 里读(链接文字与保留的非链接文字都会显示)
devecocli ui layout | Select-String 'LINK'
# ④ 点击:用 dump 里该段文字的坐标点进去,看 onPress(url, text) 的日志
hdc shell "uitest uiInput click <x> <y>"
hdc shell "hilog -x | grep hyperlink-test"
# ⑤ 依赖形态排查(装错版本时):
# node_modules/linkify-it/build/index.cjs.js 末尾是 module.exports=<函数> 还是 exports.X=<对象>
环境搭建
本手册的验证环境是 React Native for OpenHarmony(RNOH)。从零准备开发环境(DevEco Studio、HarmonyOS SDK API 26、Node.js、ohpm、hvigorw、模拟器与 hdc 调试)请按 CPF-RN 开发者指南的「环境初始化」操作:
- CPF-RN 开发者指南 · 02-搭建准备 · 环境初始化:https://atomgit.com/CPF-RN/docs/blob/main/开发者指南/02-搭建准备/环境初始化.md
环境就绪后,本次验证使用的具体版本组合见文末「验证环境」一节。
验证环境
| 项 | 版本 |
|---|---|
| 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 / npm | v24.14.0 / 11.9.0 |
| 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),1320×2232,density 3 |
| 本机状态栏高度 | 117px(页面留白取 48vp) |
| 宿主依赖 | ✅ 本轮补装 linkify-it@5.0.2 + mdurl@2.1.0(先误装 6.1.0 ⇒ 启动崩溃 ⇒ 按 ^5 重装) |
| 宿主 HAP 产物 | entry-default-signed.hap |
| 本次构建 | assembleHap 4 分 36 秒(旧断言 14/24)⇒ 修断言后失败 ⇒ hvigorw clean + 全量重建 15 分 59 秒 ⇒ 22/22 |
| 验证规模 | 设备侧 22 / 22 断言;链接化定量 4 个实例(H1/H3/H4 各 3、H2=0 对照);URL 规范化 3 种;切分文本读 dump 1 处;点击实测 1 次(url + text);依赖版本坑 1 次(复现 + 修);构建缓存坑 1 次;UI 安全区 1 项 |
相关社区与项目地址
| 项目 | 地址 |
|---|---|
| React Native 鸿蒙官方社区(CPF-RN) | https://atomgit.com/CPF-RN |
| RNOH 框架仓库 | https://atomgit.com/openharmony-RN/ohos_react_native |
| RNOH 三方库使用文档 | https://atomgit.com/OpenHarmony-RN/usage-docs |
| React Native for OpenHarmony 组织 | https://atomgit.com/oh-react-native |
| RN 三方库鸿蒙适配清单 | https://atomgit.com/oh-react-native/rn-ohos-adaptation-overview |
欢迎加入 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
更多推荐


所有评论(0)