浅析 eTS 的起源和演进
引言
Mozilla 创造了 JS,Microsoft 创建了 TS,Huawei 进一步推出了 eTS。
从最初的基础的逻辑交互能力,到具备类型系统的高效工程开发能力,再到融合声明式 UI、多维状态管理等丰富的应用开发能力,共同组成了相关的演进脉络。
eTS(extended TypeScript)是鸿蒙生态的一种应用开发语言。它在 TypeScript(简称 TS)的基础上,扩展了声明式 UI、状态管理等相应的能力,让开发者可以以更简洁、更自然的方式开发高性能应用。TS 是 JavaScript(简称 JS)的超集,eTS 则是 TS 的超集。eTS 会结合应用开发和运行的需求持续演进,包括但不限于引入分布式开发范式、并行和并发能力增强、类型系统增强等方面的语言特性。本期我们结合 JS 和 TS 以及相关的开发框架的发展,为大家介绍 eTS 的起源和演进思路。
一、JS
JS 语言由 Mozilla 创造,最初主要是为了解决页面中的逻辑交互问题,它和 HTML(负责页面内容)、CSS(负责页面布局和样式)共同组成了 Web 页面/应用开发的基础。随着 Web 和浏览器的普及,以及 Node.js 进一步将 JS 扩展到了浏览器以外的环境,JS 语言得到了飞速的发展。在 2015 年相关的标准组织 ECMA 发布了一个主要的版本 ECMAScript 6(简称 ES6),这个版本具备了较为完整的语言能力,包括类(Class)、模块(Module)、相关的语言基础 API 增强(Map/Set 等)、箭头函数(Arrow Function)等。从 2015 年开始,ECMA 每年都会发布一个标准版本,比如 ES2016/ES2017/ES2018 等,JS 语言越来越成熟。
为了提升应用的开发效率,相应的 JS 前端框架也不断地涌现出来。其中比较典型的有 Facebook 发起的 React.js,以及个人开发者尤雨溪发起的 Vue.js。React 和 Vue 的主要出发点都是将响应式编程的能力引入到应用开发中,实现数据和界面内容的自动关联处理。具体的实现方式上,React 对 JS 做了一些扩展,引入了 JSX(JavaScript XML)语法,可以将 HTML 的内容统一表示成 JS 来处理;Vue 则是通过扩展的模板语法(Template)的方式来处理。
下面通过两个示例,为大家简要介绍 React 和 Vue。(示例来源于 w3schools 网站:https://www.w3schools.com/whatis/)
1. React 示例
图 1 React
示例以上代码描述了 React 怎么在指定的页面元素(id 为 id01 的 div 元素)中改变相应的字符串内容(从"Hello World!"到"Hello John Doe!")。其中第 5 行的 ReactDOM.render()是 React JS 库提供的一个方法,它可以将相应的内容刷新到指定的 HTML 元素中。第 6 行是符合 JSX 语义的一段代码,它包含了一个类似 HTML 结构的字符串(<h1>...</h1>),以及一个表达数据绑定语义的字段({name}),会关联到第 4 行定义的 name 变量。通过这种方式,JSX 把 HTML 的语义以及数据绑定机制和 JS 语言结合起来,可以方便地在 JS 语言中使用。
2. Vue 示例
图 2 Vue 示例
以上 Vue 示例代码也描述了类似的功能。其中第 1~3 行是类似 HTML 的语法,描述一个 id 为 app 的 div 页面元素,其中的{{message}}是数据绑定的语义,在 Vue 中表示为 Template。第 6~9 行是 JS 代码,描述了一个 Vue 对象,对应了上述的 app 页面元素以及所需的数据变量 message 的内容信息。第 11~13 行则是 JS 函数,它改变 message 变量的值为"John Doe"。执行这个函数时 Vue 会自动实现相应的 UI 界面刷新。
如上所示,React 和 Vue 所表达的能力是类似的,不过侧重点稍微有所不同。React 主要是基于 JSX 的语法,将类 HTML 的语法融合到 JS 语言中;Vue 则是基于 Template 机制,在 HTML 的基础上扩展相应的语义。当然,上面这两个例子只是简要地描述了 React 和 Vue 的基础信息,更详细的语法以及 CSS 相关的使用等都没涉及。
从运行时的维度来看,基于 React 以及 Vue 的应用都可运行在 Web 引擎上。为了进一步提升相应的性能体验,2015 年 Facebook 在 React 基础上推出了 React Native, 在渲染架构上没有采用传统的 Web 引擎渲染路径,而是桥接到相应 OS 平台的原生 UI 组件上。2019 年 Facebook 引入全新实现的 JS 引擎 Hermes,并推出一系列架构改进来进一步提升 React Native 的性能体验。2016 年阿里巴巴开源的 Weex 则是基于 Vue 做了一些类似的改进,也是采用了桥接到原生 UI 组件的渲染路径。
二、TS
随着 JS 生态的发展,如何更有效地支撑大型的应用工程开发变成了一个重要的课题。大型的应用工程一般会涉及较复杂的代码以及较多的团队协作,对语言的规范性,模块的复用性、扩展性以及相关的开发工具都提出了更高的要求。为此,Microsoft 在 JS 的基础上,创建了 TS 语言,并在 2014 年正式发布了 1.0 版本。TS 主要从以下几个方面做了进一步的增强:
引入了类型系统,并提供了类型检查以及类型自动推导能力,可以进行编译时错误检查,有效的提升了代码的规范性以及错误检测范围和效率。
在类型系统基础上,引入了声明文件(Declaration Files)来管理接口或其他自定义类型。声明文件一般是以 d.ts 的形式来定义模块中的接口,这些接口和具体的实现做了相应的分离,有助于各模块之间的分工协作。另外,TS 通过接口,泛型(Generics)等相关特性的支持,进一步增强了设计复杂的框架所需的扩展以及复用能力。
在工具层面,TS 也有相应的编辑器、编译器、IDE(Integrated Development Environment)插件等相关的工具,来进一步提升开发效率。
TS 在兼容 JS 生态方面也做了较好的平衡,TS 应用通过相应编译器可以编译出纯 JS 应用,可以在标准的 JS 引擎上运行。同时,TS 定位为 JS 的超集,即 JS 应用也是合法的 TS 应用。此外,在标准层面上,TS 兼容 ECMA 的相应标准,并维护那些还未成为 ECMA 标准的新特性。
三、eTS
如上所述,基于 JS 的前端框架以及 TS 的引入,进一步提升了应用开发效率,但依然存在一些不足。
从开发者维度来看:
写一个应用需要了解三种语言(JS/TS、HTML 和 CSS)。这对 Web 开发者相对友好,但对非 Web 开发者来说,负担较重。
从运行时维度来看:
在语言运行时方面,尽管 TS 有了类型的加持,但也只是用于编译时检查,然后通过 TS Compiler 转成 JS,运行时引擎还是无法利用到基于类型系统的优化。
在渲染方面,主流 Web 引擎由于本身复杂度以及历史原因,性能、资源占用方面与常见 OS 原生框架都有一定的差距,尤其在移动平台上。React Native 通过渲染架构的改进一定程度上提升了性能体验,但在平台渲染效果和能力的一致性,以及 JS 语言性能等方面还是存在一定的不足。
Google 在 2018 年底推出的 Flutter 则走了另外一条路,结合新的语言 Dart,引入新的声明式开发范式,基于 Skia 的自绘制引擎构建可跨平台的独立的渲染能力。这是一种较为创新的方案,不过也有几点不足:
Dart 语言生态。尽管 Dart 语言 2011 年就已推出,而且目标是取代 JS,但整个生态还是非常弱小,Dart 语言发布 7 年后随着 Flutter 的推出才有所改善。整体而言,Dart 和主流语言生态相比还是有非常大的差距。
开发范式。Flutter 暴露了很多细粒度的 Widget 接口,整体开发的简洁度,开发门槛,尤其是和 Apple 推出的 SwiftUI 相比,存在一定的差距。
有意思的是,Google 在 2021 年又推出了新的开发框架 Jetpack Compose,结合了 Kotlin 的语言生态,设计了新的声明式 UI 开发范式。
2019 年,我们在思考如何构建新的应用开发框架的时候,从以下几个维度进行了重点考虑:
语言生态
开发效率
性能体验
跨设备/跨平台能力
由于 JS/TS 有比较完善的开发者生态,语言也比较中立友好,有相应的标准组织可以逐步演进,JS/TS 语言成了比较自然的选择。以 JS/TS 为基础,在开发框架的维度,我们做了如下的架构演进设计:
通过基于 JS 扩展的类 Web 开发范式,来支持主流的前端开发方式。同步的,在运行时方面,通过渲染引擎的增强(平台无关的自绘制机制、声明式 UI 后端设计、动态布局/多态 UI 组件等),语言编译器和运行时的优化增强(代码预编译、高效 FFI-Foreign Function Interface、引擎极小化等),进一步提升相关的性能体验,并可部署到不同设备上(包括百 KB 级内存的轻量设备)。另外,通过平台适配层的设计,构建了跨 OS 平台的基础设施。
通过基于 TS 扩展的声明式 UI 开发范式,提供了更简洁更自然的开发体验。在运行时方面,在上述的基础上,结合语言运行时的类型优化,以及渲染运行时的扁平化流水线技术等,进一步提升性能体验。
图 3 ArkUI 开发框架
图 3 描述了 ArkUI 开发框架的整体架构,其中,基于 TS 扩展的声明式 UI 范式中所用的语言就是 eTS。下面结合一个具体示例,从应用开发视角简单介绍下基于 eTS 的全新声明式开发范式。
如图 4 所示的代码示例,UI 界面会显示一个 “Hello World” 的文本和一个 “Click me” 按钮。当用户点击“Click me”按钮时,字符串变量 myText 的值会从“World” 变为“ACE”,文本最终显示为 “Hello ACE”。
图 4 eTS 声明式开发范式
这个示例中所包含的 eTS 声明式开发范式的基本组成说明如下:
(1) 装饰器
用来装饰类、结构体、方法以及变量,赋予其特殊的含义,如上述示例中 @Entry 、 @Component 、 @State 都是装饰器。具体而言, @Component 表示这是个自定义组件; @Entry 则表示这是个入口组件; @State 表示组件中的状态变量,这个状态变化会引起 UI 变更。
(2) 自定义组件
可复用的 UI 单元,可组合其它组件,如上述被 @Component 装饰的 struct Hello。
(3) UI 描述
声明式的方式来描述 UI 的结构,如上述 build() 方法内部的代码块。
(4) 内置组件
框架中默认内置的基础和布局组件,可直接被开发者调用,比如示例中的 Column、Text、Divider、Button。
(5) 事件方法
用于添加组件对事件的响应逻辑,统一通过事件方法进行设置,如跟随在 Button 后面的 onClick()。
(6) 属性方法
用于组件属性的配置,统一通过属性方法进行设置,如 fontSize()、width()、height()、color() 等,可通过链式调用的方式设置多项属性。
从 UI 框架的需求角度,eTS 在 TS 的类型系统的基础上,做了进一步的扩展:定义了各种装饰器、自定义组件和 UI 描述机制,再配合 UI 开发框架中的 UI 内置组件、事件方法、属性方法等共同构成了应用开发的主体。在应用开发中,除了 UI 的结构化描述之外,还有一个重要的方面:状态管理。如上述示例中,用 @State 装饰过的变量 myText ,包含了一个基础的状态管理机制,即 myText 的值的变化会自动触发相应的 UI 变更 (Text 组件)。ArkUI 中进一步提供了多维度的状态管理机制。和 UI 相关联的数据,不仅可以在组件内使用,还可以在不同组件层级间传递,比如父子组件之间,爷孙组件之间,也可以是全局范围内的传递,还可以是跨设备传递。另外,从数据的传递形式来看,可分为只读的单向传递和可变更的双向传递。开发者可以灵活的利用这些能力来实现数据和 UI 的联动。
总体而言,ArkUI 开发框架通过扩展成熟语言、结合语法糖或者语言原生的元编程能力、以及 UI 组件、状态管理等方面设计了统一的 UI 开发范式,结合原生语言能力共同完成应用开发。这些构成了当前 eTS 基于 TS 的主要扩展。
ArkUI 完整的开发范式可参考这里:
https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/ui/Readme-CN.md
四、下一步演进
接下来,除 UI 框架需求之外,eTS 也会结合应用开发及运行的其他方面需求持续演进:
1. 更完善的类型系统
我们已经设计并实现了专门运行时,利用 eTS 的类型输入,在程序执行一开始就获得较高的运行性能(不像其它传统 JS 引擎需要预热才能获取高性能)。但是目前的类型系统在运行时的设计上仍然考虑了兼容模式,即在运行时,当对象类型发生变化时会走 Bailout 机制,以使程序在类型不匹配时仍能正常运行。一种更极致的方式是:引入一种特定模式来支持确定类型的表达,当开发者可以明确类型时,提供相应的信息,这样运行时可以通过针对性设计,进一步提升性能体验。另外,eTS 将来也会在类型系统中拓展一些新的类型,在与运行时结合的优化中会提供更好的性能体验。
2. 更灵活的并行化处理
目前的移动设备基本都是多核设备(包括同一配置的多核以及不同配置的大小核),有些设备还会携带多种计算芯片(CPU/GPU/NPU/...)。语言在并发特性上如何充分应用多核设备甚至异构芯片是一个重要的课题。目前我们采用的仍然是业界常见的类 Actor 模型的并发接口——Worker,它弥补了 Actor 模型的些许劣势,即允许用户转移和共享大量的 Buffer 以避免通信时拷贝的开销。但是开发者仍需自己去管理 Worker 的生命周期,利用 Worker 也不能非常方便地触发一个异步并行任务。我们已经在尝试在 Actor 模型上封装一种任务接口,方便用户更容易利用多核触发异步并行任务。我们也一直在关注 Swift、Dart、Kotlin、Go 这些语言并发特性的发展和运行时的实现,eTS 的特定模式中静态类型模型的引入也会给并发机制带来更多高性能实现的可能性,比如对象的冻结、所有权转移、值语义等等。我们将持续致力于提供简洁高效的并发 API,帮助应用开发者更容易开发出高性能的应用。
当然,eTS 以及 ArkUI 开发框架还很年轻,还有很多其它方面也会持续演进,比如 UI 自定义能力的进一步完善,语言运行时以及跨语言交互的进一步优化,跨 OS 平台能力的扩展(包括 Android、iOS 等),分布式开发范式等等。
作为应用生态的底座,应用开发框架的创新永无止境。我们希望和广大的开发者一起,持续围绕着开发效率、运行体验、跨设备/跨平台等相关方面一起合作,一起创新,共建繁荣的应用生态!
评论