慕ke慕w-鸿蒙NEXT应用开发工程师

317 阅读7分钟

鸿蒙系统采用ArkTS作为其原生开发语言。如果你对TypeScript有所了解,那么你将能够轻松过渡到ArkTS,因为它并非一种全新的语言,而是基于TypeScript的扩展。这意味着我们可以省去学习新语言的过程,只需专注于鸿蒙的UI框架——ArkUI。

慕ke慕w-鸿蒙NEXT应用开发工程师 --- “夏のke” --- Ukoou·ㄷㅁΜ

ArkUI框架与React类似,采用声明式开发模式来构建用户界面,并内置了全面的状态管理机制,无需像React那样面对众多复杂的第三方状态管理库。

鸿蒙系统的内置组件布局设计灵感来源于CSS,包括margin、padding、flex、grid、栅格系统和媒体查询等概念,这些都是Web前端开发者所熟悉的。

鸿蒙Next提供了一套完整的开发、调试、测试和发布流程,集成在一个IDE中。如果你之前使用过IntelliJ IDEA,那么你将发现使用鸿蒙的DevEco Studio是无缝的,因为它是基于IntelliJ IDEA Community版深度定制的。即使你是Neovim或VSCode的忠实用户,DevEco Studio同样易于上手。

IDE介绍

在开始之前,让我们先了解鸿蒙的IDE——DevEco Studio。

DevEco-Studio 安装

目前,DevEco Studio提供了三个版本供下载:

  • Windows(64-bit)
  • Mac(X86)
  • Mac(ARM)

请注意,当前版本需要Node 18.x环境。对于前端开发者,建议安装nvm,并预先切换到正确的Node版本。

安装nvm后,执行以下命令:

nvm install 18.14.1
nvm use 18.14.1
node -v

这样,在安装DevEco时,你可以选择本地的Node环境。如果你的本地没有Node,也可以选择全新安装。

如果一切顺利,在Diagnose界面,你将看到所有的检查项都显示为对号,这表示安装成功。

我相信你能够自己摸索着创建一个"Hello World"项目。

接下来,你将看到项目中默认创建了大量的文件。起初,我和你一样,不知道从何下手。但经过研究,我发现许多文件都存在两个版本:一个是模块级的,另一个是应用级的。

为了更清晰地展示,我制作了一张图,图中左侧列出的文件都是应用和模块的两个版本。

整个entry目录称为一个Module,该目录将编译为一个以.hap为后缀的文件,即HAP包。

当应用最终发布时,它将被打包成一个以.app为扩展名的文件,并上传到华为应用商店。

Module不仅限于entry类型,还包括其他类型。为了简化,我仍然总结了一张图:

初看可能会感到有些困惑,但实际上,作为初学者,我们只需要关注entry模块。其他Module类型可以等到更深入的开发阶段再去文档中了解。

entry目录下,有一个src/main/ets/pages/index.ets文件,这是用户看到的第一个页面。我们先打开这个文件,稍后我将详细介绍对应的语法。

@Entry
@Component
struct Index {
  @State message: string = 'Hello World';

  build() {
    Row() {
      Column() {
        Text(this.message)
          .fontSize(50)
          .fontWeight(FontWeight.Bold)
          .width('100%')
          .height('100%')
      }
    }
  }
}

第一行的@Entry装饰器表明这是应用的入口点,build()方法返回的是整个声明式UI的页面结构。

翻译过来,就是在较高的Row组件中放置了一个较宽的Column组件,其中包含了一个文本组件,字体大小为50。

在右上角选择previewer,点击run按钮即可运行并预览效果。

输出效果

这就是整个开发流程。刚才的代码看起来是否非常熟悉?是的,它是TypeScript!

编程语言

鸿蒙的主要开发语言是ArkTS(以.ets文件为扩展名)。它看起来如此熟悉,因为它是TypeScript的一个超集,基本上是TS代码。最大的区别或特点是,在编译时对静态类型的检查和分析进行了增强,并对一些动态特性进行了一些限制。这使得程序在运行时类型都是已知的,减少了运行时错误并提高了程序性能。

例如,强制类型不允许使用anyunknown类型。也不能使用obj as any之类的语法动态给对象添加属性和方法,更不能在运行时使用delete删除属性或方法。在某些需要any类型的情况下,建议使用Record<string, Object>类型。

编译器默认开启了TypeScript的一些严格模式,例如strictPropertyInitialization,要求强制给定初始值。strictNullChecks强制进行空值安全检查等。

总之,你能想到的所有运行时的动态类型特性都将受到限制,尽量不要使用。更多详细的语法规则可以参考官方文档中关于《从TypeScript到ArkTS的适配规则》的文章,内容非常详细。实际上,在开发过程中,DevEco Studio会提供非常好的错误报告信息,因此你可以先不必了解这些语法规则,等到遇到问题时再去查询。

UI框架

ArkUI框架是基于ArkTS的UI框架,采用声明式开发范式,数据驱动UI更新,并提供了页面级路由导航等。

在页面布局方面,提供了多种媒体查询,例如:

  • 设备类型
  • 窗口宽高监听
  • 折叠屏状态
  • 横竖屏查询

统一了单位,例如:

  • vp 虚拟像素
  • fp 字体像素,用户端的设置会乘以系数 1fp = 1vp * scale

并提供了多种栅格系统,窗口栅格会根据容器宽度自动匹配栅格数量:

  • 4格:small(360~600),手机竖屏
  • 8格:medium(600~840),手机横屏,pad竖屏,折叠屏
  • 12格:large(840~1440),pad横屏,2in1
  • 12格:x-large(1440~),全屏

需要注意的是,目前稳定主推的应用模型称为Stage模型。如果你在学习过程中看到FA模型,那就是旧版教程,可以不用看了。

下面我们在代码层面了解一下如何声明UI。

声明式UI描述

声明式UI的描述方式如下:

Column() {
  Text('item 1')
  Divider()
  Text('item 2')
}

Column是容器组件,所以后面带有{},包含子组件。非容器组件则无需{}

给组件配置属性通常使用链式调用的方法:

Text(
  "hello"
).fontSize(
  20
).fontColor(
  Color.Red
).fontWeight(
  FontWeight.Bold
);

为了更加清晰,通常会写成这种格式:

Text("hello")
  .fontSize(20)
  .fontColor(Color.Red)
  .fontWeight(FontWeight.Bold);

添加事件处理

Button("Click me").onClick(() => {
  this.myText = "ArkUI";
});

装饰器

ArkUI中大量使用了装饰器,包括我们之前看到的@entry表示入口。再看一个最简单的自定义组件:

ArkUI中用@Component装饰的struct结构代表自定义组件。

@Component
struct MyComponent {
  @State message: string = 'Hello, World!';

  build() {
    // 在build函数里返回UI描述
  }
}

在当前流行的数据驱动UI编程范式中,UI = f(State),状态是不可或缺的。

ArkUI中用@State装饰器来声明状态,Parent可以直接覆盖Child State:

@Component
struct Parent {
  build() {
    Column() {
      // 父组件覆盖State
      MyComponent({ message: "hi" })
    }
  }
}

和React中的State类似,状态的变化可以引起UI更新,但注意这个状态不是immutable的,UI是否可以观察到状态的变化要看数据类型,具体要参考详细的文档。

下面是典型的事件处理中修改状态,引起UI刷新的例子:

@Component
export struct HelloComponent {
  @State message: string = 'Hello, World!';

  build() {
    Row() {
      Text(this.message)
        .onClick(() => {
          // 基础类型的状态变量message改变驱动UI刷新,
          // UI从'Hello, World!'刷新为'Hello, ArkUI!'
          this.message = 'Hello, ArkUI!';
        })
    }
  }
}

@State只是组件内部的状态。如果想在状态更改时引发Child组件的更新