一、ArkTS简介
ArkTS是HarmonyOS优选的主力应用开发语言。它在TypeScript(简称TS)的基础上,匹配了鸿蒙的ArkUI框架,扩展了声明式UI、状态管理等相应的能力,让开发者以更简洁、更自然的方式开发跨端应用。了解ArkTS之前,我们需要先了解下ArkTS、TypeScript和JavaScript之间的关系。
- JavaScript是一种属于网络的高级脚本语言,已经被广泛用于Web应用开发,常用来为网页添加各式各样的动态功能,为用户提供更流畅美观的浏览效果。
- TypeScript 是 JavaScript 的一个超集,它扩展了 JavaScript 的语法,通过在JavaScript的基础上添加静态类型定义构建而成,是一个开源的编程语言。
- ArkTS兼容TypeScript语言,拓展了声明式UI、状态管理、并发任务等能力,可以认为是TypeScript的超集。 由此可知,TypeScript是JavaScript的超集,ArkTS则是TypeScript的超集,他们的关系如下图所示。
学习ArkTS声明式的语法之前,需要对TypeScript语法有一个基本的了解,此处不过多的介绍,详情参考:ArkTS开发语言简介
二、ArkTS诞生背景
在语言运行时方面,尽管TS有了类型的加持,但也只是用于编译时检查,然后通过TS Compiler转成JS,运行时引擎还是无法利用到基于类型系统的优化。
在渲染方面,主流Web引擎由于本身复杂度以及历史原因,性能、资源占用方面与常见OS原生框架都有一定的差距,尤其在移动平台上。React Native通过渲染架构的改进一定程度上提升了性能体验,但在平台渲染效果和能力的一致性,以及JS语言性能等方面还是存在一定的不足。
接着,Google在2018年底推出的Flutter则走了另外一条路,结合新的语言Dart,引入新的声明式开发范式,基于Skia的自绘制引擎构建可跨平台的独立的渲染能力。这是一种较为创新的方案,但是仍然有一些不足,比如Dart语言增加了学习成本。有意思的是,Google在2021年又推出了新的开发框架Jetpack Compose,结合了Kotlin的语言生态,设计了新的声明式UI开发范式。
我们在思考如何构建新的应用开发框架的时候,从以下几个维度进行了重点考虑:
- 语言生态
- 开发效率
- 性能体验
- 跨设备/跨平台能力
由于JS/TS有比较完善的开发者生态,语言也比较友好,有相应的标准组织可以逐步演进,JS/TS语言成了比较自然的选择。以JS/TS为基础,在开发框架的维度,我们做了如下的架构演进设计:
- 通过基于JS扩展的类Web开发范式,来支持主流的前端开发方式。同时,在运行时方面,通过渲染引擎的增强(平台无关的自绘制机制、声明式UI后端设计、动态布局/多态UI组件等),语言编译器和运行时的优化增强(代码预编译、高效FFI-Foreign Function Interface、引擎极小化等),进一步提升相关的性能体验,并可部署到不同设备上(包括百KB级内存的轻量设备)。另外,通过平台适配层的设计,构建了跨OS平台的基础设施。
- 通过基于TS扩展的声明式UI开发范式,提供了更简洁更自然的开发体验。在运行时方面,在上述的基础上,结合语言运行时的类型优化,以及渲染运行时的扁平化流水线技术等,进一步提升性能体验。
下图描述了ArkUI开发框架的整体架构:
可以看到,基于TS扩展的声明式UI范式中所用的语言就是ArkTS。下面结合一个具体示例,从应用开发视角简单介绍下基于ArkTS的全新声明式开发范式。例如,下面代码就是ArkTS声明式开发范式(有点类似于Flutter的写法):
@Entry
@Component
struct Index {
@State message: string = 'Hello World'
build() {
Column() {
Text('Hello').fontSize(30)
Text(this.message).fontSize(30)
Button(){
Text('Click Me').fontSize(30)
}.onClick(()=>{
this.message='ArkUI'
})
.width(200)
.height(50)
}
}
}
这个示例中所包含的ArkTS声明式开发范式的基本组成说明如下:
- 装饰器 用来装饰类、结构体、方法以及变量,赋予其特殊的含义,如上述示例中 @Entry 、 @Component 、 @State 都是装饰器。具体而言, @Component 表示这是个自定义组件; @Entry 则表示这是个入口组件; @State 表示组件中的状态变量,此状态变化会引起 UI 变更。
- 自定义组件 可复用的 UI 单元,可组合其它组件,如上述被 @Component 装饰的 struct Hello。
- UI 描述 声明式的方式来描述 UI 的结构,如上述 build() 方法内部的代码块。
- 内置组件 框架中默认内置的基础和布局组件,可直接被开发者调用,比如示例中的 Column、Text、Divider、Button。
- 事件方法 用于添加组件对事件的响应逻辑,统一通过事件方法进行设置,如跟随在Button后面的onClick()。
- 属性方法
用于组件属性的配置,统一通过属性方法进行设置,如fontSize()、width()、height()、color() 等,可通过链式调用的方式设置多项属性。
总体而言,ArkUI开发框架通过扩展成熟语言、结合语法糖或者语言原生的元编程能力、以及UI组件、状态管理等方面设计了统一的UI开发范式,结合原生语言能力共同完成应用开发。
三、ArkUI规划和演进
接下来,ArkUI会从以下两个方面进行一些改进:
3.1 更完善的类型系统
已经设计并实现了专门运行时,利用ArkTS的类型输入,在程序执行一开始就获得较高的运行性能(不像其它传统JS引擎需要预热才能获取高性能)。但是目前的类型系统在运行时的设计上仍然考虑了兼容模式,即在运行时,当对象类型发生变化时会走Bailout机制,以使程序在类型不匹配时仍能正常运行。一种更极致的方式是:引入一种特定模式来支持确定类型的表达,当开发者可以明确类型时,提供相应的信息,这样运行时可以通过针对性设计,进一步提升性能体验。另外,ArkTS将来也会在类型系统中拓展一些新的类型,在与运行时结合的优化中会提供更好的性能体验。
3.2 更灵活的并行化处理
目前的移动设备基本都是多核设备(包括同一配置的多核以及不同配置的大小核),有些设备还会携带多种计算芯片(CPU/GPU/NPU/...)。语言在并发特性上如何充分应用多核设备甚至异构芯片是一个重要的课题。目前我们采用的仍然是业界常见的类Actor模型的并发接口——Worker,它弥补了Actor模型的些许劣势,即允许用户转移和共享大量的Buffer以避免通信时拷贝的开销。
但是开发者仍需自己去管理Worker的生命周期,利用Worker也不能非常方便地触发一个异步并行任务。我们已经在尝试在Actor模型上封装一种任务接口,方便用户更容易利用多核触发异步并行任务。我们也一直在关注Swift、Dart、Kotlin、Go这些语言并发特性的发展和运行时的实现,ArkTS的特定模式中静态类型模型的引入也会给并发机制带来更多高性能实现的可能性,比如对象的冻结、所有权转移、值语义等等。我们将持续致力于提供简洁高效的并发API,帮助应用开发者更容易开发出高性能的应用。
当然,ArkTS以及ArkUI开发框架还很年轻,还有很多其它方面也会持续演进,比如UI自定义能力的进一步完善,语言运行时以及跨语言交互的进一步优化,跨OS平台能力的扩展,分布式开发范式等等。
四、ArkTS开发实践
4.1 声明式UI基本概念
应用界面是由一个个页面组成,ArkTS是由ArkUI框架提供,用于以声明式开发范式开发界面的语言。声明式UI构建页面的过程,其实是组合组件的过程,声明式UI的思想,主要体现在两个方面:
- 描述UI的呈现结果,不需要关心过程;
- 状态驱动视图更新;
类似苹果的SwiftUI中通过组合视图View,安卓Jetpack Compose中通过组合@Composable函数,ArkUI作为HarmonyOS应用开发的UI开发框架,其使用ArkTS语言构建自定义组件,通过组合自定义组件完成页面的构建。
4.2 自定义组件
ArkTS通过struct声明组件名,并通过@Component和@Entry装饰器,来构成一个自定义组件。并且,使用@Entry和@Component装饰的自定义组件作为页面的入口,会在页面加载时首先进行渲染。比如:
@Entry
@Component
struct Index {
...
}
在自定义组件内需要使用build方法来进行UI描述。
@Entry
@Component
struct Index
...
build() {
...
}
}
同时,build方法内可以容纳内置组件和其他自定义组件,里面渲染的内容就是最后页面显示的内容。
4.3 配置属性与布局
自定义组件通常使用的是基础组件和容器组件等内置组件进行的组合。但有时内置组件的样式并不能满足我们的需求,ArkTS提供了属性方法用于描述界面的样式。属性方法支持以下使用方式:
- 常量传递:使用fontSize(50)来配置字体大小
Text('Hello World').fontSize(50)
- 变量传递:在组件内定义了相应的变量后,可以使用this.变量的方式使用该变量
Text('Hello World').fontSize(this.size)
- 链式调用:在配置多个属性时,ArkTS提供了链式调用的方式,通过'.'方式连续配置
Text('Hello World').fontSize(this.size).width(100).height(100)
- 内置枚举类型:ArkTS内置了很多的枚举类型,如Color,FontWeight等。
Text('Hello World').fontColor(Color.Red).fontWeight(FontWeight.Bold)
4.4 状态改变
实际开发中页面的内容并不是一层不变的,页面的内容可能需要产生变化,此时就需要用到状态管理,类似于前端的setState。不过,ArkTS改变后会自动刷新。
声明式UI的特点就是UI是随数据更改而自动刷新的,我们这里定义了一个类型为boolean的变量isComplete,其被@State装饰后,框架内建立了数据和视图之间的绑定,其值的改变影响UI的显示。
@State isComplete : boolean = false;
@State装饰器的作用如下图。
4.5 循环渲染列表
当我们有多条待办数据需要显示在页面时,就可以使用到ForEach循环渲染语法。例如,有下面一组数据:
total_Tasks:Array<string> = [
'早起晨练',
'准备早餐',
'阅读名著',
'学习ArkTS',
'看剧放松'
]
下面是一段完整的代码:
@Entry
@Component
struct ToDoList {
...
build() {
Row() {
Column() {
Text(...)
...
ForEach(this.totalTasks,(item) => {
TodoItem({content:item})
},...)
}
.width('100%')
}
.height('100%')
}
}