写 HarmonyOS 应用,绕不开 ArkTS。它是华为给 HarmonyOS 选定的官方高级语言,从 TypeScript 扩展而来,又加了一层静态类型约束和状态管理能力。很多前端同学第一次看到 ArkTS 代码会觉得眼熟,写起来又会发现有些地方跟 TS 不太一样。这篇就把 ArkTS 的定位、它从 TS 演进过来的路径、以及它和 ArkUI 的关系捋清楚。

ArkTS 的定位:HarmonyOS 应用的官方语言

HarmonyOS 应用开发不止一种语言可选,但 ArkTS 是官方主推的那一个。它的定位很明确:给 HarmonyOS 应用开发用的官方高级语言。

这个定位意味着两件事:

第一,ArkTS 是面向应用开发的语言,不是系统级语言。写驱动、写底层服务不是它的活儿,那是 C/C++ 的地盘。ArkTS 写的是应用层的 UI、业务逻辑、数据交互。

第二,它是"高级语言",带类型系统、带装饰器、带完整的工程化能力。不是脚本语言的定位,而是一套有编译工具链、有运行时、有完整生态的语言。

ArkTS 在 TypeScript 生态基础上做了进一步扩展,保持了 TS 的基本风格,同时通过规范定义强化开发期静态检查和分析,提升代码健壮性,并实现更好的程序执行稳定性和性能。

从 TypeScript 到 ArkTS 的演进路径

要理解 ArkTS,得先看它从 TS 演进过来的脉络。这个演进不是推倒重来,而是在 TS 基础上做加减法。

加入静态类型

强化静态检查
加入状态管理

配合 ArkUI

JavaScript

TypeScript

ArkTS

HarmonyOS 应用

演进的核心思路有三条:

第一条,强化静态类型。 TS 的类型系统是可选的,可以写 any,可以关闭严格检查,可以用 @ts-ignore 跳过类型检查。ArkTS 把这些口子都堵上了。any 不让用,unknown 不让用,@ts-ignore 不让用,严格类型检查不是可配置项而是强制的。这么做是为了让编译器能在编译期就把类型问题查出来,减少运行时开销。

第二条,加入状态管理和 UI 描述能力。 TS 是纯语言,不关心 UI。ArkTS 引入了一套装饰器体系,@State@Prop@Link@Component@Entry 这些,专门用来描述 UI 组件和组件间的状态同步。这是 ArkTS 区别于 TS 最明显的地方。

第三条,增强并发能力。 TS/JS 的并发能力支持有限,主要靠 Promise 和 async/await,真正的多线程并发(Worker)用起来比较重。ArkTS 在这基础上加了 TaskPool 和 Worker 两套并发 API,还提出了 Sendable 概念来支持对象在并发实例间的引用传递,提升并发通信性能。

ArkTS 的核心特性

把 ArkTS 摊开看,核心特性有这么几块:

静态类型与严格检查

ArkTS 强制静态类型,编译期就把类型确定下来。这跟 TS 的"渐进式类型"不一样,TS 的类型是给你看的,编译完就擦除了;ArkTS 的类型会参与到编译优化和运行时性能里。

// ArkTS 中不能写 any
// let value: any = 42;  // 编译报错

// 必须显式标注类型,或者让编译器能推断出来
let value: number = 42;
let name: string = 'ArkTS';

// 对象字面量要标注类型
class UserInfo {
  public name: string = '';
  public age: number = 0;
}

let user: UserInfo = { name: '张三', age: 28 };

状态管理装饰器

这是 ArkTS 最有特色的部分。一套装饰器描述 UI 组件的状态和关系:

@Entry
@Component
struct Counter {
  @State count: number = 0;  // 状态变量,变化会触发 UI 刷新

  build() {
    Column() {
      Text(`点击次数: ${this.count}`)
        .fontSize(20)

      Button('加一')
        .onClick(() => {
          this.count++;
        })
    }
  }
}

@State 标记的变量一变,依赖它的 UI 自动刷新。@Prop 做单向同步,@Link 做双向同步。这套机制让 UI 和状态绑定在一起,不用手动调 refresh。

并发能力增强

ArkTS 提供了 TaskPool 和 Worker 两种并发 API,还引入了 Sendable 概念:

import { taskpool } from '@kit.ArkTS';

@Concurrent
function heavyCompute(data: number[]): number {
  // 在子线程跑的耗时计算
  return data.reduce((sum, n) => sum + n, 0);
}

// 主线程调用
let task = new taskpool.Task(heavyCompute, [1, 2, 3, 4, 5]);
taskpool.execute(task).then((result: Object) => {
  console.info(`计算结果: ${result}`);
});

基础类库增强

ArkTS 的基础类库和容器类库比标准 TS 更丰富,提供了高精度数学库 Decimal、Buffer 与 FastBuffer、XML 处理、多种容器库等能力。这些是写应用时常用的工具,官方给你备好了。

与 ArkUI 的关系

ArkTS 和 ArkUI 经常一起出现,容易搞混两者的关系。简单说:ArkTS 是语言,ArkUI 是框架

ArkUI 是 HarmonyOS 的 UI 开发框架,提供了一套声明式的 UI 描述语法。这套语法的载体就是 ArkTS。你在 ArkTS 代码里写的 build() 方法、Column()Text()Button() 这些,就是 ArkUI 提供的组件和 API。

// 这段代码里,ArkTS 提供语言能力,ArkUI 提供 UI 组件
@Entry
@Component
struct MyPage {
  @State message: string = 'Hello';

  build() {  // ArkUI 的组件构建方法
    Column() {  // ArkUI 的布局组件
      Text(this.message)  // ArkUI 的文本组件
        .fontSize(16)
    }
  }
}

两者的分工是:ArkTS 负责类型系统、状态管理装饰器、语言语义;ArkUI 负责组件体系、布局算法、渲染管线。状态管理装饰器(@State 等)是 ArkTS 语言层面的能力,但服务的对象是 ArkUI 的 UI 刷新机制。

与 TS/JS 的互操作

ArkTS 支持与 TS/JS 高效互操作。这意味着你可以复用已有的 TS/JS 代码库,不用全部重写。互操作的场景包括:

  • 在 ArkTS 项目里引用 TS/JS 模块
  • ArkTS 代码调用 TS/JS 导出的函数
  • TS/JS 代码调用 ArkTS 导出的 API

不过互操作有边界。跨语言调用会有一定开销,性能敏感的路径不建议频繁跨语言。另外 ArkTS 的静态类型约束在互操作时仍然生效,从 TS/JS 那边拿到的数据要符合 ArkTS 的类型要求。

编译运行:从源码到执行

ArkTS 代码写完,是怎么跑起来的?这背后是方舟编译运行时(ArkCompiler)在干活。ArkCompiler 分两部分:

ArkCompiler

编译工具链

运行时

ArkTS 源码

方舟字节码 .abc

设备上执行

ArkTS 编译工具链

ArkTS 运行时

ArkTS 编译工具链负责把高级语言编译成方舟字节码文件(*.abc)。ArkTS 运行时负责在设备侧运行字节码文件,执行程序逻辑。运行时里有解释器、AOT 编译器、JIT 编译器等多种执行方式,根据场景选择最优路径。

这块的细节后面有专门的文章展开讲,这里先知道个大概:ArkTS 代码不是直接解释执行的,会先编译成字节码,再由运行时根据热点情况决定解释执行还是编译执行。

一个完整的 ArkTS 组件示例

把前面讲的特性串起来看一个完整的例子。这是一个带状态管理、异步请求、列表渲染的组件:

import { taskpool } from '@kit.ArkTS';

// 数据模型
class TodoItem {
  public id: number = 0;
  public title: string = '';
  public done: boolean = false;
}

// 模拟异步数据加载
@Concurrent
function fetchTodos(): TodoItem[] {
  // 实际场景里这里会是网络请求或数据库查询
  let result: TodoItem[] = [];
  for (let i = 1; i <= 5; i++) {
    let item: TodoItem = { id: i, title: `任务 ${i}`, done: false };
    result.push(item);
  }
  return result;
}

@Entry
@Component
struct TodoListPage {
  @State todos: TodoItem[] = [];
  @State loading: boolean = false;

  aboutToAppear() {
    this.loadTodos();
  }

  async loadTodos() {
    this.loading = true;
    try {
      let task = new taskpool.Task(fetchTodos);
      let result = await taskpool.execute(task) as TodoItem[];
      this.todos = result;
    } catch (e) {
      console.error(`加载失败: ${e}`);
    } finally {
      this.loading = false;
    }
  }

  build() {
    Column() {
      if (this.loading) {
        Text('加载中...')
          .fontSize(16)
      } else {
        ForEach(this.todos, (item: TodoItem) => {
          Row() {
            Text(item.title)
              .fontSize(16)
              .layoutWeight(1)
            Toggle({ type: ToggleType.Checkbox, isOn: item.done })
              .onChange((isOn: boolean) => {
                item.done = isOn;
              })
          }
          .padding(12)
        }, (item: TodoItem) => item.id.toString())
      }
    }
  }
}

这段代码里用到了 ArkTS 的多个特性:类定义和静态类型、@State 状态管理、@Concurrent 标记并发函数、TaskPool 异步执行、async/await 异步语法、ArkUI 的组件和布局。一个文件里把这些串起来,就是典型的 ArkTS 应用代码组织方式。

ArkTS 的演进方向

ArkTS 不是定死不变的。官方文档里提到,未来会结合应用开发和运行的需求持续演进,逐步提供并发能力增强、系统类型增强、分布式开发范式等更多特性。

从现在的形态看,ArkTS 的设计取向很明确:宁可限制一些灵活性,也要换来性能和稳定性。这个取向跟 HarmonyOS 的设备场景有关——HarmonyOS 要跑在从手机到穿戴到车机等各种设备上,性能和稳定性比语言灵活性更重要。理解了这个取向,就能理解 ArkTS 为什么做了那些限制。

小小总结

别把 ArkTS 当 TS 写。 语法像不代表习惯能照搬。ArkTS 的类型约束更严,any 不能用,对象字面量要标类型,解构赋值不支持。从 TS 项目迁过来要先过类型检查这一关。

状态管理装饰器是 ArkTS 的核心。 写 HarmonyOS 应用,@State@Prop@Link 这套机制要吃透。它不是可选的语法糖,而是 ArkUI 刷新 UI 的基础。理解了状态变量的依赖追踪,才能写出性能好的组件。

并发能力按场景选。 简单的 I/O 异步用 Promise/async/await 就够;CPU 密集的短任务用 TaskPool;长时间运行的后台任务用 Worker。别一上来就 Worker,杀鸡用牛刀。

互操作不是免费的。 ArkTS 调 TS/JS 有跨语言开销,热点路径别频繁跨。能纯 ArkTS 实现的逻辑就别混着写。

官方文档要常看。 ArkTS 还在演进,特性在增,限制在调。写代码遇到编译报错先查文档,可能是某个特性被限制了,也可能是新版本有更好的写法。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐