鸿蒙的 ArkTS/ArkUI 和 Web、React Native 有什么区别?Web 前端页面怎样适配鸿蒙?
ArkTS 在 TypeScript 基础上强制静态类型、禁止运行时改对象布局,不支持 any、JSX、解构声明等写法;ArkUI 用 struct 自定义组件、链式属性和装饰器状态变量声明界面。Web 页面在鸿蒙上由基于 Chromium 的 ArkWeb 组件显示,适配重点是按 UA 里的 OpenHarmony 识别设备、按系统版本对应的内核查兼容性、通过 javaScriptProxy 和 runJavaScript 与应用互相调用。
简答
鸿蒙原生应用默认用 ArkTS 写。ArkTS 是在 TypeScript 基础上扩展出来的,风格和 TS 很像,但规则更严:必须用静态类型,不能用
any,运行时不能给对象加删属性,JSX、var、解构声明这些写法也不支持。界面用 ArkUI 的声明式写法,组件是一个带@Component的struct,在build()里用Column()、Text()这类系统组件拼界面,样式用.fontSize()这样链式调用。它和 React、Vue 的思路相同,都是“状态变了,界面自动跟着变”。区别在于状态要用
@State这类装饰器标出来,没有装饰的变量改了界面也不刷新;界面最后画的是 ArkUI 自己的组件,不是 DOM。React Native 也不生成 DOM,它在运行时创建 Android、iOS 的原生视图,但写的仍是 JavaScript 和 JSX。Web 前端更常碰到的是另一件事:H5 页面在鸿蒙应用里由 ArkWeb 的
Web组件显示。ArkWeb 基于 Chromium,不同系统版本对应不同内核,比如 HarmonyOS 6.0 默认 M132,所以查兼容性要按内核版本查。页面识别鸿蒙设备,官方建议看 UA 里的OpenHarmony关键字。页面和应用互相调用也有固定方式。应用用
javaScriptProxy()或registerJavaScriptProxy()把对象注入页面,页面直接调用;应用调页面用runJavaScript()。注册时要配合deleteJavaScriptRegister防止内存泄漏,调试要先在应用里调用setWebDebuggingAccess()打开开关。
ArkTS 比 TypeScript 多了哪些限制
ArkTS 是 HarmonyOS 应用的默认开发语言。官方的说法是,它在 TypeScript 生态的基础上做了扩展,保持 TS 的基本风格,从 API version 10 起进一步强化了开发期的静态检查。主要的差异有四条:强制使用静态类型、禁止在运行时改变对象布局、限制部分运算符的语义(例如一元 + 只能用于数字)、不支持结构化类型(structural typing)。
为什么要这样限制?因为类型和对象结构在运行前都确定了,编译器能提前验证代码,运行时就能少做类型检查,官方把这一点归为性能上的考虑。所以 ArkTS 不支持一切“运行时才知道长什么样”的写法。
“从TypeScript到ArkTS的适配规则”列了每条规则的名字,其中级别为“错误”的不遵守就编译失败。前端开发者最容易撞上的几条如下,每条后面是规则名:
- 不能用
any和unknown(arkts-no-any-unknown)。 - 不能用
var,改用let(arkts-no-var)。 - 不支持解构变量声明(
arkts-no-destruct-decls)。 - 不支持函数表达式,改用箭头函数(
arkts-no-func-expressions)。 - 不支持
in运算符,因为对象布局在编译时已知(arkts-no-in)。 - 不支持 JSX(
arkts-no-jsx)。 - 不支持结构化类型,形状相同的两个类型不能互相赋值(
arkts-no-structural-typing)。
文档也说明,按这些约束改过的代码仍然是合法的 TS 代码,没有被约束的 TS 特性 ArkTS 完全支持。另外 ArkTS 兼容 TS/JS 生态,可以用 TS/JS 开发或复用已有代码,具体限制见官方的“兼容 TS/JS 的约束”。
ArkUI 的组件和状态怎么写
ArkUI 提供两种开发范式:基于 ArkTS 的声明式开发范式,和兼容 JS 的类 Web 开发范式(HML、CSS、JavaScript 三段式)。官方推荐新应用用声明式范式,理由之一是它不需要 JS 框架管理页面 DOM,渲染更新链路更短。
下面是官方“创建自定义组件”里的例子,点一下文字,界面从 Hello, World! 变成 Hello, ArkUI!:
@Component
struct HelloComponent {
@State message: string = 'Hello, World!';
build() {
Row() {
Text(this.message)
.fontSize(20)
.margin(10)
.onClick(() => {
this.message = 'Hello, ArkUI!';
})
}
}
}
拿它和 React 对比,读者会注意到几处不同。组件不是函数也不是 class,而是 struct 加 @Component;页面入口组件还要再加 @Entry。界面写在 build() 里,创建系统组件不用 new,也不写标签,直接 Text(this.message)。样式和事件都用“.”链式调用,官方建议每个属性方法单独一行。
状态这一点最要注意。“状态管理概述”写明,自定义组件里的变量必须用装饰器装饰才能成为状态变量,状态变量改变才会刷新界面;不加装饰器,界面只在初始化时渲染一次。这和 React 每次渲染重新执行函数、Vue 用响应式代理自动追踪都不一样。状态管理目前分 V1、V2 两个版本,官方建议新应用直接用 V2;而且状态管理只能在 UI 主线程使用,不能在 Worker、TaskPool 里用。
和 Web、React Native 的差别放在一起看
三者都是“写组件,描述界面应该长什么样”,差别在于最后画出来的东西和用的语言。
Web 页面画的是 DOM,由浏览器排版渲染,语言是 JavaScript,样式是 CSS。React Native 用 JavaScript 和 React 写,官方文档说明它在运行时为组件创建对应的 Android、iOS 原生视图,详见《React Native 与浏览器里的 React 有哪些共同点和差异?》。ArkUI 声明式范式用 ArkTS 写,界面由 ArkUI 框架的组件和渲染引擎绘制,同样没有 DOM,也没有 CSS 文件,样式通过组件的属性方法设置。
所以 React 或 Vue 的代码不能直接搬到 ArkUI 里。组件的拆分方式和“状态驱动界面”的思路可以沿用,语法、样式和状态声明要按 ArkTS 重写。
H5 页面在鸿蒙应用里怎样运行
更常见的情况是,鸿蒙应用里嵌了 H5 页面。显示网页用的是 ArkWeb(方舟 Web)提供的 Web 组件,访问在线网页时应用要声明 ohos.permission.INTERNET 权限。
ArkWeb 基于 Chromium 内核开发,系统版本和内核版本的对应关系由官方表格给出,例如:HarmonyOS 4.0 及之前是 M99,4.1 到 5.1 是 M114,6.0 默认 M132(也可切换到 M114)。官方建议按内核版本到 MDN 等网站查 W3C 标准的支持情况。所以评估某个 CSS 或 JS 特性能不能用,先确认目标设备的系统版本,再看对应 Chromium 版本是否支持。
怎样识别鸿蒙设备
ArkWeb 的默认 UA 格式是:
Mozilla/5.0 ({DeviceType}; {OSName} {OSVersion}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/{ChromeCompatibleVersion}.0.0.0 Safari/537.36 ArkWeb/{ArkWeb VersionCode} {DeviceCompat} {扩展区}
DeviceType 取值是 Phone、Tablet、PC(2in1 设备),OSName 默认是 OpenHarmony,手机上 DeviceCompat 默认是 Mobile。官方推荐用 OpenHarmony 关键字识别鸿蒙设备,理由是 ArkWeb 表示用的 Web 内核,OpenHarmony 才表示操作系统。下面用官方文档里的示例 UA 演示:
function parseHarmonyUA(ua) {
const match = /\((Phone|Tablet|PC); OpenHarmony ([\d.]+)\)/.exec(ua);
if (!match) return null;
return { deviceType: match[1], osVersion: match[2] };
}
const ua =
'Mozilla/5.0 (Phone; OpenHarmony 5.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36 ArkWeb/4.1.6.1 Mobile';
const info = parseHarmonyUA(ua);
console.log(info.deviceType); // Phone
console.log(info.osVersion); // 5.0
console.log(String(parseHarmonyUA('Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X)'))); // null
为什么不只看有没有 Mobile?因为平板和 PC 上 DeviceCompat 默认为空,只看 Mobile 会把它们当成桌面浏览器。文档还提到一个相关的细节:ArkWeb 根据 UA 里有没有 Mobile 决定是否启用页面 meta 标签的 viewport 属性,没有 Mobile 时默认关闭,应用可以显式设置 metaViewport 为 true 打开。所以平板上布局不对时,要先确认 viewport 是否生效。
页面和应用怎样互相调用
这和《Hybrid 开发是什么?JSBridge 的 URL Scheme 拦截和注入对象有什么区别?》里的“注入对象”是同一种思路。应用侧有两种注册方式:Web 组件初始化时用 javaScriptProxy(),初始化完成后用 registerJavaScriptProxy(),后者在下次加载或重新加载后生效。两种方式都要配合 deleteJavaScriptRegister 使用,防止内存泄漏。
假设应用把一个对象以 testObjName 的名字注册,并在 methodList 里列出 test,页面就能像调用全局对象一样调用它,这是官方示例里页面侧的写法:
<script>
function callArkTS() {
let str = testObjName.test();
document.getElementById('demo').innerHTML = str;
}
</script>
反过来,应用调用页面里的函数用 runJavaScript() 或 runJavaScriptExt(),后者除了字符串还支持 ArrayBuffer 参数。页面只要把函数挂在全局,比如 function htmlTest() {},应用就能用 runJavaScript('htmlTest()') 执行。
注入时还可以传 permission 参数,官方示例用它按 scheme、host 等限定哪些网址可以调用哪些方法。
怎样调试
用电脑上的 DevTools 调试真机网页的通用做法见《移动端页面怎样在真机上调试?Safari Web Inspector、chrome://inspect 和 vConsole 各适合什么场景?》。区别是要先让应用调用 setWebDebuggingAccess() 打开 Web 调试开关,否则 DevTools 发现不了页面;设备需要 4.1.0 及以上版本,从 API version 20 开始还支持指定端口的无线调试。
常见追问
为什么 ArkTS 不支持 any 和运行时加属性?
官方给的理由是性能和正确性。类型和对象布局都在编译时确定,编译器就能提前检查错误,运行时也少做类型检查。代价是很多 JS 的动态写法要改成用类或接口声明类型,比如对象字面量要有明确的类型,判断成员是否存在不能用
in,要用instanceof。
已有的 H5 业务要上鸿蒙,最省事的办法是什么?
用 ArkWeb 的
Web组件直接加载已有页面,原生能力通过javaScriptProxy注入的对象提供。页面侧要做的是按OpenHarmony识别设备、按系统版本对应的 Chromium 内核检查兼容性,再确认平板和 PC 上 viewport 是否生效。需要原生体验的页面再用 ArkUI 重写。
ArkUI 的类 Web 开发范式是不是就能直接跑 Vue 或 React?
不是。类 Web 范式用的是 HML 标签、CSS 和 JavaScript 三段式写法,只是更贴近 Web 开发习惯,不是浏览器环境。而且官方推荐新应用用声明式范式;在 Stage 模型下,类 Web 范式只支持卡片,应用页面只支持声明式范式。