目录
1、TypeScript快速入门
1.1、编程语言介绍
1.2、底子类型
1.3、条件语句
1.4、函数
1.5、类
1.6、模块
1.7、迭代器
2、ArkTs 底子(浅析ArkTS的起源和演进)
2.1、弁言
2.2、JS
2.3、TS
2.4、ArkTS
2.5、下一步演进
3、ArkTs 开发实践
3.1、声明式UI基本概念
3.2、自界说组件的组成
3.4、配置属性与布局
3.5、改变组件状态
3.6、循环渲染列表数据
通过本章节的学习,您可以掌握基于 TS 扩展的 ArkTS 语言,以更靠近天然语义快速开发应用。
1、TypeScript快速入门
TypeScript 是一个开源的编程语言,本章节只介绍了TypeScript的底子语法知识,更多内容大家可以参考 TypeScript 的官方教程(https://www.typescriptlang.org/docs/)。大家在学习过程中,假如没有搭建TypeScript的开发环境,也可以直接使用在线 Playground 平台(https://www.typescriptlang.org/play)举行编码训练。没有接触过 TypeScript 的同学可以先补齐相关的语法底子,再进入 HarmonyOS 的相关开发学习之旅。
1.1、编程语言介绍
ArkTS 是 HarmonyOS 优选的主力应用开发语言。它在 TypeScript(简称TS)的底子上,匹配ArkUI 框架,扩展了声明式UI、状态管理等相应的本领,让开发者以更轻巧、更天然的方式开发跨端应用。要了解什么是ArkTS,我们首先要了解下ArkTS、TypeScript 和 JavaScript之间的关系:
- JavaScript是一种属于网络的高级脚本语言,已经被广泛用于Web应用开发,常用来为网页添加各式各样的动态功能,为用户提供更流畅美观的浏览效果。
- TypeScript 是 JavaScript 的一个超集,它扩展了 JavaScript 的语法,通过在 JavaScript 的底子上添加静态类型界说构建而成,是一个开源的编程语言。
- ArkTS 兼容 TypeScript 语言,拓展了声明式UI、状态管理、并发使命等本领。
由此可知,TypeScript是JavaScript的超集,ArkTS则是TypeScript的超集,他们的关系如下图所示:
在学习ArkTS声明式的相关语法之前,我们首先学习下TypeScript的底子语法。
1.2、底子类型
TypeScript支持一些底子的数据类型,如布尔型、数组、字符串等,下面举例几个较为常用的数据类型,我们来了解下他们的基本使用。
布尔值
TypeScript中可以使用boolean来表示这个变量是布尔值,可以赋值为true或者false。
- let isDone: boolean = false;
复制代码 数字
TypeScript里的所有数字都是浮点数,这些浮点数的类型是 number。除了支持十进制,还支持二进制、八进制、十六进制。
- let decLiteral: number = 2023;
- let binaryLiteral: number = 0b11111100111;
- let octalLiteral: number = 0o3747;
- let hexLiteral: number = 0x7e7;
复制代码 字符串
TypeScript里使用 string表示文本数据类型, 可以使用双引号( ")或单引号(')表示字符串。
- let name: string = "Jacky";
- name = "Tom";
- name = 'Mick';
复制代码 数组
TypeScrip有两种方式可以界说数组。 第一种,可以在元素类型后面接上 [],表示由此类型元素组成的一个数组。
- let list: number[] = [1, 2, 3];
复制代码 第二种方式是使用数组泛型,Array<元素类型>。
- let list: Array<number> = [1, 2, 3];
复制代码 元组
元组类型允许表示一个已知元素数目和类型的数组,各元素的类型不必相同。 好比,你可以界说一对值分别为 string和number类型的元组。
- let x: [string, number];
- x = ['hello', 10]; // OK
- x = [10, 'hello']; // Error
复制代码 罗列
enum类型是对 JavaScript 标准数据类型的一个补充,使用罗列类型可以为一组数值赋予友好的名字。
- enum Color {Red, Green, Blue};
- let c: Color = Color.Green;
复制代码 Unknown
有时候,我们会想要为那些在编程阶段还不清楚类型的变量指定一个类型。这种情况下,我们不盼望类型检查器对这些值举行检查而是直接让它们通过编译阶段的检查。那么我们可以使用unknown类型来标记这些变量。
- let notSure: unknown = 4;
- notSure = 'maybe a string instead';
- notSure = false;
复制代码 当一个函数没有返回值时,你通常会见到其返回值类型是 void。
- function test(): void {
- console.log('This is function is void');
- }
复制代码 Null 和 Undefined
TypeScript里,undefined 和 null 两者各自有自己的类型分别叫做 undefined 和 null。
- let u: undefined = undefined;
- let n: null = null;
复制代码 联合类型
联合类型(Union Types)表示取值可以为多种类型中的一种。
- let myFavoriteNumber: string | number;
- myFavoriteNumber = 'seven';
- myFavoriteNumber = 7;
复制代码 1.3、条件语句
条件语句用于基于差别的条件来实行差别的动作。TypeScript 条件语句是通过一条或多条语句的实行效果(True 或 False)来决定实行的代码块。
if 语句
TypeScript if 语句由一个布尔表达式后跟一个或多个语句组成。
- var num:number = 5
- if (num > 0) {
- console.log('数字是正数')
- }
复制代码 if...else 语句
一个 if 语句后可跟一个可选的 else 语句,else 语句在布尔表达式为 false 时实行。
- var num:number = 12;
- if (num % 2==0) {
- console.log('偶数');
- } else {
- console.log('奇数');
- }
复制代码 if...else if....else 语句
if...else if....else 语句在实行多个判断条件的时候很有效。
- var num:number = 2
- if(num > 0) {
- console.log(num+' 是正数')
- } else if(num < 0) {
- console.log(num+' 是负数')
- } else {
- console.log(num+' 为0')
- }
复制代码 switch…case 语句
一个 switch 语句允许测试一个变量即是多个值时的情况。每个值称为一个 case,且被测试的变量会对每个 switch case 举行检查。
- var grade:string = 'A';
- switch(grade) {
- case 'A': {
- console.log('优');
- break;
- }
- case 'B': {
- console.log('良');
- break;
- }
- case 'C': {
- console.log('及格');
- break;
- }
- case 'D': {
- console.log('不及格');
- break;
- }
- default: {
- console.log('非法输入');
- break;
- }
- }
复制代码 1.4、函数
函数是一组一起实行一个使命的语句,函数声明要告诉编译器函数的名称、返回类型和参数。TypeScript 可以创建著名字的函数和匿名函数,其创建方法如下:
- // 有名函数
- function add(x, y) {
- return x + y;
- }
- // 匿名函数
- let myAdd = function (x, y) {
- return x + y;
- };
复制代码 为函数界说类型
为了确保输入输出的准确性,我们可以为上面那个函数添加类型:
- // 有名函数:给变量设置为number类型
- function add(x: number, y: number): number {
- return x + y;
- }
- // 匿名函数:给变量设置为number类型
- let myAdd = function (x: number, y: number): number {
- return x + y;
- };
复制代码 可选参数
在TypeScript里我们可以在参数名旁使用 ?实现可选参数的功能。 好比,我们想让lastName是可选的:
- function buildName(firstName: string, lastName?: string) {
- if (lastName)
- return firstName + ' ' + lastName;
- else
- return firstName;
- }
- let result1 = buildName('Bob');
- let result2 = buildName('Bob', 'Adams');
复制代码 剩余参数
剩余参数会被当做个数不限的可选参数。 可以一个都没有,同样也可以有任意个。 可以使用省略号( ...)举行界说:
- function getEmployeeName(firstName: string, ...restOfName: string[]) {
- return firstName + ' ' + restOfName.join(' ');
- }
- let employeeName = getEmployeeName('Joseph', 'Samuel', 'Lucas', 'MacKinzie');
复制代码 箭头函数
ES6版本的TypeScript提供了一个箭头函数,它是界说匿名函数的简写语法,用于函数表达式,它省略了function关键字。箭头函数的界说如下,其函数是一个语句块:
- ( [param1, parma2,…param n] )=> {
- // 代码块
- }
复制代码 其中,括号内是函数的入参,可以有0到多个参数,箭头后是函数的代码块。我们可以将这个箭头函数赋值给一个变量,如下所示:
- let arrowFun = ( [param1, parma2,…param n] )=> {
- // 代码块
- }
复制代码 如何要主动调用这个箭头函数,可以按如下方法去调用:
- arrowFun(param1, parma2,…param n)
复制代码 接下来我们看看如何将我们认识的函数界说方式转换为箭头函数。我们可以界说一个判断正负数的函数,如下:
- function testNumber(num: number) {
- if (num > 0) {
- console.log(num + ' 是正数');
- } else if (num < 0) {
- console.log(num + ' 是负数');
- } else {
- console.log(num + ' 为0');
- }
- }
复制代码 其调用方法如下:
- testNumber(1) //输出日志:1 是正数
复制代码 假如将这个函数界说为箭头函数,界说如下所示:
- let testArrowFun = (num: number) => {
- if (num > 0) {
- console.log(num + ' 是正数');
- } else if (num < 0) {
- console.log(num + ' 是负数');
- } else {
- console.log(num + ' 为0');
- }
- }
复制代码 其调用方法如下:
- testArrowFun(-1) //输出日志:-1 是负数
复制代码 后面,我们在学习HarmonyOS应用开发时会常常用到箭头函数。例如,给一个按钮添加点击变乱,其中onClick变乱中的函数就是箭头函数。
- Button("Click Now")
- .onClick(() => {
- console.info("Button is click")
- })
复制代码 1.5、类
TypeScript 支持基于类的面向对象的编程方式,界说类的关键字为 class,后面紧跟类名。类描述了所创建的对象共同的属性和方法。
类的界说
例如,我们可以声明一个Person类,这个类有3个成员:一个是属性(包罗name和age),一个是构造函数,一个是getPersonInfo方法,其界说如下所示。
- class Person {
- private name: string
- private age: number
- constructor(name: string, age: number) {
- this.name = name;
- this.age = age;
- }
- public getPersonInfo(): string {
- return `My name is ${this.name} and age is ${this.age}`;
- }
- }
复制代码 通过上面的Person类,我们可以界说一个人物Jacky并获取他的基本信息,其界说如下:
- let person1 = new Person('Jacky', 18);
- person1.getPersonInfo();
复制代码 继续
继续就是子类继续父类的特征和行为,使得子类具有父类相同的行为。TypeScript中允许使用继续来扩显现有的类,对应的关键字为 extends 。
- class Employee extends Person {
- private department: string
- constructor(name: string, age: number, department: string) {
- super(name, age);
- this.department = department;
- }
- public getEmployeeInfo(): string {
- return this.getPersonInfo() + ` and work in ${this.department}`;
- }
- }
复制代码 通过上面的Employee类,我们可以界说一个人物Tom,这里可以获取他的基本信息,也可以获取他的雇主信息,其界说如下:
- let person2 = new Employee('Tom', 28, 'HuaWei');
- person2.getPersonInfo();
- person2.getEmployeeInfo();
复制代码 在TypeScript中,有public、private、protected修饰符,其功能和具体使用场景大家可以参考TypeScript 的相关学习资料,举行拓展学习。
1.6、模块
随着应用越来越大,通常要将代码拆分成多个文件,即所谓的模块(module)。模块可以相互加载,并可以使用特殊的指令 export 和 import 来交换功能,从另一个模块调用一个模块的函数。
两个模块之间的关系是通过在文件级别上使用 import 和 export 建立的。模块里面的变量、函数和类等在模块外部是不可见的,除非明确地使用 export 导出它们。雷同地,我们必须通过 import 导入其他模块导出的变量、函数、类等。
导出
任何声明(好比变量,函数,类,类型别名或接口)都能够通过添加export关键字来导出,例如我们要把NewsData这个类导出,代码表示如下:
- export class NewsData {
- title: string;
- content: string;
- imagesUrl: Array<NewsFile>;
- source: string;
- constructor(title: string, content: string, imagesUrl: Array<NewsFile>, source: string) {
- this.title = title;
- this.content = content;
- this.imagesUrl = imagesUrl;
- this.source = source;
- }
- }
复制代码 导入
模块的导入操纵与导出一样简朴。 可以使用以下 import形式之一来导入别的模块中的导出内容。
- import { NewsData } from '../common/bean/NewsData';
复制代码 1.7、迭代器
当一个对象实现了Symbol.iterator属性时,我们以为它是可迭代的。一些内置的类型如Array,Map,Set,String,Int32Array,Uint32Array等都具有可迭代性。
for..of 语句
for..of会遍历可迭代的对象,调用对象上的Symbol.iterator方法。 下面是在数组上使用for..of的简朴例子:
- let someArray = [1, "string", false];
- for (let entry of someArray) {
- console.log(entry); // 1, "string", false
- }
复制代码 for..of vs. for..in 语句
for..of 和 for..in 均可迭代一个列表,但是用于迭代的值却差别:for..in迭代的是对象的键,而for..of则迭代的是对象的值。
- let list = [4, 5, 6];
- for (let i in list) {
- console.log(i); // "0", "1", "2",
- }
- for (let i of list) {
- console.log(i); // "4", "5", "6"
- }
复制代码 2、ArkTs 底子(浅析ArkTS的起源和演进)
2.1、弁言
Mozilla创造了JS,Microsoft创建了TS,Huawei进一步推出了ArkTS。
从最初的底子的逻辑交互本领,到具备类型系统的高效工程开发本领,再到融合声明式UI、多维状态管理等丰富的应用开发本领,共同组成了相关的演进脉络。
ArkTS 是HarmonyOS优选的主力应用开发语言。它在TypeScript(简称TS)的底子上,扩展了声明式UI、状态管理等相应的本领,让开发者可以以更轻巧、更天然的方式开发高性能应用。TS是JavaScript(简称JS)的超集,ArkTS则是TS的超集。ArkTS会联合应用开发和运行的需求一连演进,包罗但不限于引入分布式开发范式、并行和并发本领增强、类型系统增强等方面的语言特性。本期我们联合JS和TS以及相关的开发框架的发展,为大家介绍ArkTS的起源和演进思路。
2.2、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网站:Web Development)
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组件的渲染路径。
2.3、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标准的新特性。
2.4、ArkTS
如上所述,基于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范式中所用的语言就是ArkTS。下面联合一个具体示例,从应用开发视角简朴介绍下基于ArkTS的全新声明式开发范式。
如图4所示的代码示例,UI界面会表现两段文本和一个按钮,当开发者点击按钮时,文本内容会从'Hello World'变为‘Hello ArkUI’。
图4 ArkTS声明式开发范式
这个示例中所包罗的ArkTS声明式开发范式的基本组成说明如下:
用来装饰类、结构体、方法以及变量,赋予其特殊的含义,如上述示例中 @Entry 、 @Component 、 @State 都是装饰器。具体而言, @Component 表示这是个自界说组件; @Entry 则表示这是个入口组件; @State 表示组件中的状态变量,此状态变革会引起 UI 变更。
可复用的 UI 单位,可组合别的组件,如上述被 @Component 装饰的 struct Hello。
声明式的方式来描述 UI 的结构,如上述 build() 方法内部的代码块。
框架中默认内置的底子和布局组件,可直接被开发者调用,好比示例中的 Column、Text、Divider、Button。
用于添加组件对变乱的响应逻辑,统一通过变乱方法举行设置,如跟随在Button后面的onClick()。
用于组件属性的配置,统一通过属性方法举行设置,如fontSize()、width()、height()、color() 等,可通过链式调用的方式设置多项属性。
从UI框架的需求角度,ArkTS在TS的类型系统的底子上,做了进一步的扩展:界说了各种装饰器、自界说组件和UI描述机制,再配合UI开发框架中的UI内置组件、变乱方法、属性方法等共同构成了应用开发的主体。在应用开发中,除了UI的结构化描述之外,还有一个紧张的方面:状态管理。如上述示例中,用 @State 装饰过的变量 myText ,包罗了一个底子的状态管理机制,即 myText 的值的变革会主动触发相应的 UI 变更 (Text组件)。ArkUI 中进一步提供了多维度的状态管理机制。和 UI 相关联的数据,不但可以在组件内使用,还可以在差别组件层级间通报,好比父子组件之间,爷孙组件之间,也可以是全局范围内的通报,还可以是跨装备通报。另外,从数据的通报形式来看,可分为只读的单向通报和可变更的双向通报。开发者可以灵活的利用这些本领来实现数据和 UI 的联动。
总体而言,ArkUI开发框架通过扩展成熟语言、联合语法糖或者语言原生的元编程本领、以及UI组件、状态管理等方面计划了统一的UI开发范式,联合原生语言本领共同完成应用开发。这些构成了当前ArkTS基于TS的紧张扩展。
ArkUI完备的开发范式可参考这里:
文档中心https://developer.harmonyos.com/cn/docs/documentation/doc-guides/arkui-overview-0000001281480754
2.5、下一步演进
接下来,除UI框架需求之外,ArkTS也会联合应用开发及运行的其他方面需求一连演进:
1. 更美满的类型系统
我们已经计划并实现了专门运行时,利用ArkTS的类型输入,在程序实行一开始就得到较高的运行性能(不像别的传统JS引擎需要预热才华获取高性能)。但是如今的类型系统在运行时的计划上仍然考虑了兼容模式,即在运行时,当对象类型发生变革时会走Bailout机制,以使程序在类型不匹配时仍能正常运行。一种更极致的方式是:引入一种特定模式来支持确定类型的表达,当开发者可以明确类型时,提供相应的信息,这样运行时可以通过针对性计划,进一步提升性能体验。另外,ArkTS将来也会在类型系统中拓展一些新的类型,在与运行时联合的优化中会提供更好的性能体验。
2. 更灵活的并行化处理
如今的移动装备基本都是多核装备(包罗同一配置的多核以及差别配置的大小核),有些装备还会携带多种盘算芯片(CPU/GPU/NPU/...)。语言在并发特性上如何充分应用多核装备乃至异构芯片是一个紧张的课题。如今我们接纳的仍然是业界常见的类Actor模型的并发接口——Worker,它弥补了Actor模型的些许劣势,即允许用户转移和共享大量的Buffer以避免通信时拷贝的开销。但是开发者仍需自己去管理Worker的生命周期,利用Worker也不能非常方便地触发一个异步并行使命。我们已经在实验在Actor模型上封装一种使命接口,方便用户更容易利用多核触发异步并行使命。我们也不停在关注Swift、Dart、Kotlin、Go这些语言并发特性的发展和运行时的实现,ArkTS的特定模式中静态类型模型的引入也会给并发机制带来更多高性能实现的可能性,好比对象的冻结、所有权转移、值语义等等。我们将一连致力于提供轻巧高效的并发API,帮助应用开发者更容易开发出高性能的应用。
当然,ArkTS以及ArkUI开发框架还很年轻,还有很多别的方面也会一连演进,好比UI自界说本领的进一步美满,语言运行时以及跨语言交互的进一步优化,跨OS平台本领的扩展,分布式开发范式等等。
作为应用生态的底座,应用开发框架的创新永无止境。我们盼望和广大的开发者一起,一连围绕着开发效率、运行体验、跨装备/跨平台等相关方面一起合作,一起创新,共建繁荣的应用生态。
3、ArkTs 开发实践
3.1、声明式UI基本概念
应用界面是由一个个页面组成,ArkTS是由ArkUI框架提供,用于以声明式开发范式开发界面的语言。
声明式UI构建页面的过程,其实是组合组件的过程,声明式UI的思想,紧张体如今两个方面:
- 描述UI的出现效果,而不关心过程
- 状态驱动视图更新
雷同苹果的 SwiftUI 中通过组合视图View,安卓 Jetpack Compose 中通过组合@Composable函数,ArkUI作为HarmonyOS应用开发的UI开发框架,其使用 ArkTS 语言构建自界说组件,通过组合自界说组件完成页面的构建。
3.2、自界说组件的组成
ArkTS通过 struct 声明组件名,并通过@Component和@Entry装饰器,来构成一个自界说组件。
使用@Entry和@Component装饰的自界说组件作为页面的入口,会在页面加载时首先举行渲染。
- @Entry
- @Component
- struct ToDoList {...}
复制代码 例如ToDoList组件对应如下整个代办页面。
图1 ToDoList待办列表
使用@Component装饰的自界说组件,如ToDoItem这个自界说组件则对应如下内容,作为页面的组成部门。
- @Component
- struct ToDoItem {...}
复制代码 图2 ToDoItem
在自界说组件内需要使用build方法来举行UI描述。
- @Entry
- @Component
- struct ToDoList
- ...
- build() {
- ...
- }
- }
复制代码 build 方法内可以容纳内置组件和其他自界说组件,如Column和Text都是内置组件,由ArkUI框架提供,ToDoItem为自界说组件,需要开发者使用ArkTS自行声明。
- @Entry
- @Component
- struct ToDoList {
- ...
- build() {
- Column(...) {
- Text(...)
- ...
- ForEach(...{
- TodoItem(...)
- },...)
- }
- ...
- }
- }
复制代码 3.4、配置属性与布局
自界说组件的组成使用底子组件和容器组件等内置组件举行组合。但有时内置组件的样式并不能满意我们的需求,ArkTS提供了属性方法用于描述界面的样式。属性方法支持以下使用方式:
- 常量通报例如使用fontSize(50)来配置字体大小。
- Text('Hello World')
- .fontSize(50)
复制代码
- 变量通报在组件内界说了相应的变量后,例如组件内部成员变量size,就可以使用this.size方式使用该变量。
- Text('Hello World')
- .fontSize(this.size)
复制代码
- 链式调用在配置多个属性时,ArkTS提供了链式调用的方式,通过'.'方式一连配置。
- Text('Hello World')
- .fontSize(this.size) .width(100) .height(100)
复制代码
- 表达式通报属性中还可以传入普通表达式以及三目运算表达式。
- Text('Hello World')
- .fontSize(this.size) .width(this.count + 100) .height(this.count % 2 === 0 ? 100 : 200)
复制代码
- 内置罗列类型除此之外,ArkTS中还提供了内置罗列类型,如Color,FontWeight等,例如设置fontColor改变字体颜色为赤色,并私有fontWeight为加粗。
- Text('Hello World')
- .fontSize(this.size) .width(this.count + 100) .height(this.count % 2 === 0 ? 100 : 200) .fontColor(Color.Red) .fontWeight(FontWeight.Bold)
复制代码 对于有多种组件需要举行组合时,容器组件则是描述了这些组件应该如何分列的效果。
ArkUI中的布局容器有很多种,在差别的适用场合选择差别的布局容器实现,ArkTS使用容器组件接纳花括号语法,内部放置UI描述。
这里我们将介绍最底子的两个布局——列布局和行布局。
对于如下每一项的布局,两个元素为横向分列,选择Row布局
图3 Row布局
- Row() {
- Image($r('app.media.ic_default'))
- ...
- Text(this.content)
- ...
- }
- ...
复制代码 雷同下图所示的布局,整体都是从上往下纵向分列,适用的布局方式是Column列布局。
图4 Column布局
- Column() {
- Text($r('app.string.page_title'))
- ...
- ForEach(this.totalTasks,(item) => {
- TodoItem({content:item})
- },...)
- }
复制代码 3.5、改变组件状态
实际开发中由于交互,页面的内容可能需要产生变革,以每一个ToDoItem为例,其在完成时的状态与未完成时的展示效果是不一样的。
图5 差别状态的视图
声明式UI的特点就是UI是随数据更改而主动刷新的,我们这里界说了一个类型为boolean的变量isComplete,其被@State装饰后,框架内建立了数据和视图之间的绑定,其值的改变影响UI的表现。
- @State isComplete : boolean = false;
复制代码 图6 @State装饰器的作用
用圆圈和对勾这样两个图片,分别来表示该项是否完成,这部门涉及到内容的切换,需要使用条件渲染if / else语法来举行组件的表现与消散,当判断条件为真时,组件为已完成的状态,反之则为未完成。
- if (this.isComplete) {
- Image($r('app.media.ic_ok'))
- .objectFit(ImageFit.Contain)
- .width($r('app.float.checkbox_width'))
- .height($r('app.float.checkbox_width'))
- .margin($r('app.float.checkbox_margin'))
- } else {
- Image($r('app.media.ic_default'))
- .objectFit(ImageFit.Contain)
- .width($r('app.float.checkbox_width'))
- .height($r('app.float.checkbox_width'))
- .margin($r('app.float.checkbox_margin'))
- }
复制代码 由于两个Image的实现具有大量重复代码,ArkTS提供了@Builder装饰器,来修饰一个函数,快速生成布局内容,从而可以避免重复的UI描述内容。这里使用@Bulider声明白一个labelIcon的函数,参数为url,对应要传给Image的图片路径。
- @Builder labelIcon(url) {
- Image(url)
- .objectFit(ImageFit.Contain)
- .width($r('app.float.checkbox_width'))
- .height($r('app.float.checkbox_width'))
- .margin($r('app.float.checkbox_margin'))
- }
复制代码 使用时只需要使用this关键字访问@Builder装饰的函数名,即可快速创建布局。
- if (this.isComplete) {
- this.labelIcon($r('app.media.ic_ok'))
- } else {
- this.labelIcon($r('app.media.ic_default'))
- }
复制代码 为了让待办项带给用户的体验更符合已完成的效果,给内容的字体也增加了相应的样式变革,这里使用了三目运算符来根据状态变革修改其透明度和文字样式,如opacity是控制透明度,decoration是文字是否有划线。通过isComplete的值来控制其变革。
- Text(this.content)
- ...
- .opacity(this.isComplete ? CommonConstants.OPACITY_COMPLETED : CommonConstants.OPACITY_DEFAULT)
- .decoration({ type: this.isComplete ? TextDecorationType.LineThrough : TextDecorationType.None })
复制代码 最后,为了实现与用户交互的效果,在组件上添加了onClick点击变乱,当用户点击该待办项时,数据isComplete的更改就能够触发UI的更新。
- @Component
- struct ToDoItem {
- @State isComplete : boolean = false;
- @Builder labelIcon(icon) {...}
- ...
- build() {
- Row() {
- if (this.isComplete) {
- this.labelIcon($r('app.media.ic_ok'))
- } else {
- this.labelIcon($r('app.media.ic_default'))
- }
- ...
- }
- ...
- .onClick(() => {
- this.isComplete= !this.isComplete;
- })
- }
- }
复制代码 3.6、循环渲染列表数据
刚刚只是完成了一个ToDoItem组件的开发,当我们有多条待办数据需要表现在页面时,就需要使用到ForEach循环渲染语法。
例如这里我们有五条待办数据需要展示在页面上。
- total_Tasks:Array<string> = [
- '早起晨练',
- '准备早餐',
- '阅读名著',
- '学习ArkTS',
- '看剧放松'
- ]
复制代码 ForEach 基本使用中,只需要了解要渲染的数据以及要生成的UI内容两个部门,例如这里要渲染的数组为以上的五条待服务项,要渲染的内容是 ToDoItem 这个自界说组件,也可以是其他内置组件。
图7 ForEach基本使用
ToDoItem这个自界说组件中,每一个ToDoItem要表现的文本参数content需要外部传入,参数通报使用花括号的形式,用content继承数组内的内容项item。
终极完成的代码及其效果如下。
- @Entry
- @Component
- struct ToDoList {
- ...
- build() {
- Row() {
- Column() {
- Text(...)
- ...
- ForEach(this.totalTasks,(item) => {
- TodoItem({content:item})
- },...)
- }
- .width('100%')
- }
- .height('100%')
- }
- }
复制代码 图8 ToDoList页面
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |