4. ArkTS语言入门:从Java/Kotlin到ArkTS

说实话,第一次接触ArkTS的时候,我心里是有点抵触的。毕竟在安卓圈摸爬滚打了七八年,Java和Kotlin早就成了肌肉记忆。但当我真正上手写了几个页面之后,嗯,真香了。

这一章,我就带你快速过一遍ArkTS的核心。咱们不搞学院派那套,直接说人话。

4.1 从Java/Kotlin到TypeScript:你其实已经会了一半

先给你吃颗定心丸:如果你写过Kotlin,那ArkTS对你来说就是换个写法的事。ArkTS基于TypeScript,而TypeScript说白了就是带类型的JavaScript。

我见过很多安卓开发者看到TypeScript就发怵,觉得是前端的东西。其实不然。你想想看,Kotlin和TypeScript的很多设计理念是相通的:

  • 类型系统:Kotlin有可空类型,TypeScript有联合类型和可选链
  • Lambda表达式:Kotlin的 { it -> doSomething() } 对应TypeScript的 => doSomething()
  • 数据类:Kotlin的 data class 对应TypeScript的 interfaceclass

核心认知:ArkTS不是让你重新学一门语言,而是让你用熟悉的思维写新的语法。

4.2 TypeScript基础速通:够用就行

我不打算把TypeScript的所有特性都讲一遍,那得再开一门课。咱们只挑ArkTS开发中最常用的几个点。

4.2.1 类型声明

在Kotlin里你写 val name: String = "Hello",在TypeScript里你写:

let name: string = "Hello"
const age: number = 25
let isActive: boolean = true

注意,TypeScript用 letconst 代替了 var。我个人习惯能用 const 就用 const,减少变量被意外修改的风险。我在项目中遇到过因为用了 var 导致作用域污染的问题,排查了半天才发现是变量提升搞的鬼。

4.2.2 接口与类型别名

Kotlin的 data class 在TypeScript里可以用 interfacetype 来替代:

// 接口方式(推荐)
interface User {
  id: number
  name: string
  email?: string  // 可选属性
}

// 类型别名方式
type User = {
  id: number
  name: string
  email?: string
}

我个人更倾向于用 interface,因为它在扩展性上更好。但如果你需要联合类型,那就得用 type

4.2.3 函数与箭头函数

Kotlin的函数定义和TypeScript的箭头函数,其实是一回事:

// Kotlin
fun greet(name: String): String {
  return "Hello, $name"
}

// TypeScript
const greet = (name: string): string => {
  return `Hello, ${name}`
}

这里有个小坑:TypeScript的模板字符串用的是反引号 `,不是单引号。我第一次写的时候习惯性地用了单引号,结果编译报错,愣是看了半天才反应过来。

小技巧:如果你觉得箭头函数不习惯,也可以用普通函数声明:function greet(name: string): string { ... }。ArkTS都支持。

4.3 ArkTS的声明式UI语法:这才是重头戏

好了,语言基础说完了。现在咱们聊聊ArkTS最核心的东西——声明式UI。

如果你用过Jetpack Compose,那你会觉得ArkTS的写法非常亲切。如果你没用过,也没关系,我带你从零开始。

4.3.1 组件结构

在ArkTS里,一个页面就是一个 @Component 装饰的类:

@Component
struct HelloPage {
  build() {
    Column() {
      Text("Hello, ArkTS!")
        .fontSize(24)
        .fontWeight(FontWeight.Bold)
      
      Button("点击我")
        .onClick(() => {
          console.info("按钮被点击了")
        })
    }
    .padding(16)
  }
}

你看,整个UI就是一层层嵌套的结构。 Column 相当于垂直布局, Text 是文本, Button 是按钮。每个组件后面可以链式调用一堆方法,比如 .fontSize().padding(),用来设置样式。

我曾经在项目里看到有人把样式写成了这样:

Text("Hello")
Text("World")
  .fontSize(20)

结果第二个Text的样式没生效。为什么?因为ArkTS的链式调用只作用于前一个组件,不能跨组件。嗯,这里要注意,每个组件的样式要单独设置。

4.3.2 状态管理:@State 和 @Prop

声明式UI的核心是状态驱动。说白了,就是数据变了,UI自动跟着变。

在ArkTS里,用 @State 装饰的变量就是状态:

@Component
struct CounterPage {
  @State count: number = 0

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

每次点击按钮, count 加1, Text 自动更新。你不需要手动去调用 setText() 或者 notifyDataSetChanged(),框架帮你做了。

如果你想把数据传给子组件,就用 @Prop

@Component
struct ChildComponent {
  @Prop message: string

  build() {
    Text(this.message)
  }
}

@Entry
@Component
struct ParentPage {
  build() {
    ChildComponent({ message: "来自父组件的数据" })
  }
}

注意@State 是组件内部的状态,@Prop 是从父组件传进来的数据。不要搞混了。我见过有人把 @Prop@State 用,结果在子组件里修改了数据,父组件那边完全不知道,排查了半天。

4.3.3 列表渲染:ForEach

在安卓里你要写 RecyclerView + Adapter,在ArkTS里只需要一个 ForEach

@Component
struct ListPage {
  @State items: string[] = ["苹果", "香蕉", "橘子"]

  build() {
    Column() {
      ForEach(this.items, (item: string, index: number) => {
        Text(`${index + 1}. ${item}`)
          .fontSize(18)
          .padding(8)
      })
    }
  }
}

你看,不需要 ViewHolder,不需要 onBindViewHolder,一个循环搞定。而且数据变了,列表自动刷新。

不过要注意, ForEach 的第二个参数是回调函数,里面返回的是UI组件。如果你需要复杂的列表项,可以单独抽成一个组件。

4.4 知识体系总览

为了让你对ArkTS的整体结构有个直观印象,我画了一张图:

ArkTS 知识体系 TypeScript 基础 类型、接口、函数 声明式UI 组件、布局、样式 状态管理 @State、@Prop、@Link 核心特性:响应式更新、组件化、跨平台 Text / Button Column / Row List / ForEach Image / Input 从语言基础到声明式UI,再到状态管理和常用组件,层层递进

4.5 避坑指南

最后,分享几个我踩过的坑,希望能帮你省点时间:

  • 不要直接修改数组:在ArkTS里,如果你用 this.items[0] = "新值",UI不会刷新。必须用 this.items.splice(0, 1, "新值") 或者重新赋值整个数组。
  • @State 只监听第一层:如果你的状态是一个对象,修改对象的属性不会触发UI更新。需要把整个对象重新赋值。
  • build() 方法里不要写耗时操作:build() 是同步的,里面不能有网络请求或者文件读写。这些操作要放到 aboutToAppear() 生命周期里。

我的建议:刚开始学ArkTS的时候,别急着写复杂页面。先拿几个简单的组件练手,比如写个计数器、待办列表。等你熟悉了声明式UI的思维模式,再去看官方文档,会发现很多东西都是相通的。

好了,ArkTS的入门就到这里。记住,语言只是工具,核心是声明式UI的思维方式。你写Kotlin的经验不会白费,只是换了一种表达方式而已。


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