3、ArkTS语言基础:TypeScript快速回顾、ArkTS声明式语法、状态管理机制

好,咱们进入第三章。说实话,ArkTS 是鸿蒙开发绕不开的第一道坎。很多从 Java 或前端转过来的同学,第一眼看到 ArkTS 会觉得「这不就是 TypeScript 吗?」—— 嗯,对,也不全对。

ArkTS 基于 TypeScript,但做了不少裁剪和增强。我刚开始接触时也踩过坑,比如装饰器用错了、状态没刷新。今天咱们就把这三个核心点捋清楚:TypeScript 快速回顾、ArkTS 声明式语法、状态管理机制。

ArkTS 语言基础 TypeScript 快速回顾 声明式语法 状态管理机制 类型注解 接口/泛型 装饰器 @Component @Builder @State @Prop/@Link

3.1 TypeScript 快速回顾

ArkTS 的语法核心就是 TypeScript。如果你写过 TS,上手会很快。但 ArkTS 做了减法 —— 它去掉了 any 类型、去掉了 enum,也去掉了部分高级类型体操。说白了,ArkTS 更强调「安全」和「可预测」。

3.1.1 类型注解

我个人习惯,写 ArkTS 时第一件事就是给变量加上类型。别偷懒,类型就是文档。

// 基础类型
let name: string = '鸿蒙'
let version: number = 3.0
let isStable: boolean = true

// 数组和联合类型
let devices: string[] = ['手机', '平板', '手表']
let status: 'online' | 'offline' = 'online'

// 对象类型
interface DeviceInfo {
  id: number
  name: string
  battery: number
}
let myDevice: DeviceInfo = { id: 1, name: 'MatePad', battery: 85 }
💡 我的习惯: 定义接口时,字段名用驼峰,类型尽量精确。别用 any —— 在 ArkTS 里你也没法用。

3.1.2 接口与泛型

接口在 ArkTS 里用得非常多。尤其是定义组件 Props 的时候。泛型呢?我建议你掌握基础用法就行,不用钻太深。

// 泛型函数
function getFirst<T>(arr: T[]): T | undefined {
  return arr[0]
}

// 泛型接口
interface ResponseData<T> {
  code: number
  data: T
  message: string
}

// 实际使用
let userResponse: ResponseData<{ name: string; age: number }> = {
  code: 200,
  data: { name: '张三', age: 28 },
  message: '成功'
}
⚠️ 注意: ArkTS 不支持 anyunknown。如果你需要灵活类型,用泛型约束替代。

3.1.3 装饰器

装饰器是 ArkTS 的「灵魂」。TypeScript 里装饰器还是实验特性,但在 ArkTS 里它是核心语法。你写组件、管理状态、定义样式,都离不开它。

// 一个简单的装饰器示例
function log(target: any, key: string) {
  console.log(`调用方法: ${key}`)
}

class Calculator {
  @log
  add(a: number, b: number): number {
    return a + b
  }
}

不过在实际的 ArkTS 开发中,你更多会用到框架内置的装饰器,比如 @Component@State@Prop 这些。咱们下面细说。

3.2 ArkTS 声明式语法

ArkTS 的 UI 写法是声明式的。什么意思?你描述「界面长什么样」,框架负责「怎么画出来」。这和命令式(比如传统 Java UI)完全不同。

我记得第一次用声明式写 UI 时,最大的感受是:代码量少了一半。你想想看,不用再写 findViewById、不用手动 setText,多清爽。

3.2.1 @Component 和 @Entry

每个页面都是一个组件。用 @Component 装饰一个 struct,用 @Entry 标记入口。

@Entry
@Component
struct HelloPage {
  build() {
    Column() {
      Text('你好,鸿蒙')
        .fontSize(24)
        .fontWeight(FontWeight.Bold)
      Button('点击我')
        .onClick(() => {
          console.log('按钮被点击了')
        })
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
  }
}
💡 避坑指南: 我曾经在 build() 方法里写条件判断,结果界面死活不刷新。后来发现 build() 里不能写 if 以外的逻辑语句。条件渲染要用 if/else 表达式,不能写 for 循环 —— 循环用 ForEach

3.2.2 @Builder 自定义构建函数

当 UI 结构重复时,别复制粘贴。用 @Builder 抽出来。

@Entry
@Component
struct CardList {
  @Builder
  CardItem(title: string, desc: string) {
    Column() {
      Text(title).fontSize(18).fontWeight(FontWeight.Medium)
      Text(desc).fontSize(14).fontColor('#666')
    }
    .padding(16)
    .backgroundColor('#f5f5f5')
    .borderRadius(8)
    .margin({ bottom: 12 })
  }

  build() {
    Column() {
      this.CardItem('分布式数据', '跨设备数据同步方案')
      this.CardItem('分布式调用', '远程服务调用机制')
    }
    .padding(16)
  }
}

3.2.3 链式调用与布局

ArkTS 的 UI 写法是链式的。每个组件后面可以跟一串 .属性().事件()。布局容器主要有 Column(纵向)、Row(横向)、Flex(弹性)、Stack(层叠)。

Row() {
  Image($r('app.media.icon'))
    .width(40)
    .height(40)
    .borderRadius(20)
  Column() {
    Text('设备名称').fontSize(16)
    Text('在线').fontSize(12).fontColor('#52c41a')
  }
  .margin({ left: 12 })
}
.padding(12)
.backgroundColor('#fff')
.borderRadius(12)
.shadow({ radius: 4, color: '#0000001a' })
核心思想: 声明式 UI 的核心是「数据驱动视图」。你只管改数据,界面自动更新。别手动操作 DOM。

3.3 状态管理机制

状态管理是 ArkTS 最值得花时间理解的部分。说白了,就是「数据变了,UI 怎么跟着变」。我刚开始做鸿蒙项目时,就因为状态没搞对,界面死活不刷新。后来发现是装饰器用错了。

3.3.1 @State:组件内部状态

@State 是最常用的。它标记的变量一旦变化,组件会重新渲染。

@Entry
@Component
struct Counter {
  @State count: number = 0

  build() {
    Column() {
      Text(`计数: ${this.count}`)
        .fontSize(28)
      Button('加一')
        .onClick(() => {
          this.count++
        })
    }
  }
}

嗯,这里要注意:@State 只能装饰简单类型或普通对象。如果你装饰一个数组,修改数组元素时不会触发刷新。得用 Array<T> 配合展开运算符或 newArray 赋值。

⚠️ 我曾经踩过的坑:@State 装饰了一个对象,然后直接修改对象的属性(比如 this.user.name = '新名字')。界面没变!因为 ArkTS 检测不到属性级别的变化。正确做法是重新赋值整个对象:this.user = { ...this.user, name: '新名字' }

3.3.2 @Prop:父传子

子组件想接收父组件的数据?用 @Prop。它是单向数据流,子组件不能修改父组件的数据。

@Component
struct ChildComponent {
  @Prop title: string

  build() {
    Text(this.title)
      .fontSize(20)
  }
}

@Entry
@Component
struct ParentComponent {
  @State message: string = '来自父组件的数据'

  build() {
    Column() {
      ChildComponent({ title: this.message })
    }
  }
}

3.3.3 @Link:双向绑定

如果子组件需要修改父组件的数据,用 @Link。它建立双向绑定,子组件改了,父组件也跟着变。

@Component
struct InputField {
  @Link text: string

  build() {
    TextInput({ placeholder: '请输入...', text: this.text })
      .onChange((value: string) => {
        this.text = value
      })
  }
}

@Entry
@Component
struct FormPage {
  @State inputValue: string = ''

  build() {
    Column() {
      InputField({ text: $inputValue })
      Text(`你输入的是: ${this.inputValue}`)
    }
  }
}
💡 注意:@Link 时要用 $ 符号,比如 $inputValue。这是 ArkTS 的语法糖,表示传递引用而不是值。

3.3.4 @Provide 和 @Consume:跨层级传递

当组件嵌套很深时,一层层传 @Prop 太麻烦。用 @Provide 在祖先组件提供数据,用 @Consume 在后代组件消费数据。不需要中间组件传递。

@Entry
@Component
struct GrandParent {
  @Provide themeColor: string = '#1890ff'

  build() {
    Column() {
      ParentComponent()
    }
  }
}

@Component
struct ParentComponent {
  build() {
    Column() {
      ChildComponent()
    }
  }
}

@Component
struct ChildComponent {
  @Consume themeColor: string

  build() {
    Text('主题色')
      .fontColor(this.themeColor)
  }
}

3.3.5 @Observed 和 @ObjectLink:深度监听

如果你有一个复杂对象,嵌套了好几层,想监听内部属性的变化怎么办?用 @Observed 装饰类,用 @ObjectLink 在子组件中引用。

@Observed
class UserModel {
  name: string = ''
  age: number = 0
}

@Entry
@Component
struct UserPage {
  @State user: UserModel = new UserModel()

  build() {
    Column() {
      UserDetail({ user: this.user })
      Button('修改年龄')
        .onClick(() => {
          this.user.age++
          this.user = this.user  // 触发刷新
        })
    }
  }
}

@Component
struct UserDetail {
  @ObjectLink user: UserModel

  build() {
    Text(`姓名: ${this.user.name}, 年龄: ${this.user.age}`)
  }
}
总结一下状态管理:
  • @State:组件自己的状态
  • @Prop:父传子,单向
  • @Link:父子双向绑定
  • @Provide/@Consume:跨层级传递
  • @Observed/@ObjectLink:深度监听复杂对象

好了,ArkTS 的基础就这些。你想想看,其实核心就是三件事:类型安全、声明式 UI、状态驱动。把这三点吃透了,后面写分布式应用就会顺手很多。

我个人建议,学完这一章后,打开 DevEco Studio 新建一个项目,把上面的代码挨个敲一遍。别光看,动手才是最快的。


公众号:蓝海资料掘金营,微信deep3321