本文记录把 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-RNhttps://atomgit.com/CPF-RN(原样)
hi@example.commailto:hi@example.com(补了 mailto:)
example.orghttp://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 本次验证的边界

  1. linkDefault 的真实跳转未验证(会尝试 Linking.openURL 跳出应用,本轮故意不触发);
  2. onLongPress 未实测(H4 只用来验证"它也算三者之一 ⇒ 会切分");
  3. linkify prop 换成自定义实例未验证(只从源码看 getDerivedStateFromProps 的同步逻辑);
  4. isTextNested 的抛错未复现(故意不触发 —— 被捕获的 TypeError 在 RNOH 上仍会整屏红屏);
  5. 嵌套 <Text> / 深层 children 的递归 parse 未逐层验证(只验证了单层 <Text> 与三处链接);
  6. Link 的样式只验证了"能生效"(红色 [LINK] 可见),未逐像素核对 linkStyle;
  7. style 透传、key/ref 过滤等边界未逐项验证;
  8. @types/* 两个依赖只在编译期用,运行期未验证(本工程未做类型检查构建);
  9. 只在一台模拟器上验证(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.commailto:hi@example.com
example.orghttp://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 断言通过。

最值得记下来的是这几条:

  1. ★★ 依赖必须"按库声明的版本范围"补装,不能图省事装最新版。
    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)即好 ✓

  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]。
  3. ★★ 导出的类不是实现类。 src/index.tsx 里具名导出与默认导出指向同一个薄包装类,真正实现的内层类没有被导出 ⇒ 它的 defaultProps、getDerivedStateFromProps、isTextNested 从外部都取不到(实测全是 undefined)。⇒ 写断言前先确认"导出的是哪一个类"。

  4. ★★ 构建失败后要先 clean,不要连着重试。 我遇到 BUILD FAILED(56 秒)后直接重试,仍然失败(47 秒),报的是 Cannot find name 'px2vp' —— 那是 oh_modules 框架代码的告警,成功构建里也有。hvigorw clean 后再构建即成功(但花了约 16 分钟全量重建)。

  5. ★ 分别检查"打包"与"构建"的退出码(老坑重现)。本次一次作业里 npm run dev 是 exit=0,而 hvigorw 是 BUILD FAILED ⇒ 我装的是旧 HAP,页面上跑的是一堆早已改掉的旧断言。

  6. ★ 预扫正则要能匹配 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
适配 TAG0.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 Native0.84.1
React19.2.3
RNOH(npm / ohpm)@react-native-oh/react-native-harmony / @rnoh/react-native-openharmony 0.84.3
Node.js / npmv24.14.0 / 11.9.0
DevEco Studio26.0.0.621
HarmonyOS SDKAPI 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

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐