ArkTS 语言与状态管理
鸿蒙 UI 开发的语法基础。ArkTS 是 ArkUI 框架的应用开发语言:以 TypeScript 为基线的静态类型子集(禁用大部分动态能力),再叠加装饰器实现「状态驱动 UI」。示例基于 ArkTS / HarmonyOS NEXT 语境,需在 DevEco Studio 中验证。
ArkTS 与 TypeScript 的差异
官方定位是「TypeScript 的超集」但落地是收敛:ArkTS 只保留类型化写法,把 TS 的动态特性视为反模式并给出编译告警/错误。要改掉四个 TS 习惯:
- 无
any/unknown逃避:所有变量需有确定类型。不确定类型用union(如string | null)或Record表达,而不是as any过关; - 禁用鸭子类型:对象必须来自明确声明(class/interface/类型别名),不接收运行时「摸出来」的额外属性。后端 JSON 一律显式声明 interface 后再映射;
- 禁止对象字面量当类型推导主体:
let o = { a: 1 }中o类型会退化为字面量类型,后续o.b = 2编译报错——需要先声明 interface/class; - 函数式能力受限:部分 TS 高阶工具类型与结构性推断不可用,写业务时更依赖
class与接口组合。
一个「声明模型 + 取数」的示例:
typescript
interface Article {
id: string
title: string
summary?: string
}
class ArticleService {
async fetchList(): Promise<Article[]> {
const resp = await fetch('https://api.example.com/articles')
const data = await resp.json() as Article[]
return data
}
}要点:接口声明数据形状;as 断言收敛类型;async/await 全程可用。装饰器是 ArkTS 给类与变量加「能力」的入口,UI 状态全部靠它驱动。
装饰器体系:一类是「这是什么」,一类是「状态怎么变」
ArkTS 装饰器分两类心智:
- 类型/入口装饰器:
@Component标记 UI 组件、@Entry标记页面入口组件、@Builder标记自定义构建函数、@Styles/@Extend做样式复用; - 状态装饰器:
@State(组件私有)、@Prop(父传子只读)、@Link(父子双向)、@ObjectLink(类对象深监听)、@Provide/@Consume(跨层共享)、@StorageLink等(与 AppStorage 同步)。
typescript
@Component
struct Counter {
@State count: number = 0
build() {
Row({ space: 8 }) {
Button('减').onClick(() => { this.count-- })
Text(`${this.count}`).fontSize(24)
Button('加').onClick(() => { this.count++ })
}
}
}V1 vs V2 要分清:经典装饰器(@State/@Prop/@Link…)是 V1 体系;API 12 起新增 V2 装饰器(@ComponentV2/@Local/@Param/@Once/@Event/@ObservedV2/@Trace)。新项目建议直接学 V2:粒度更细(@Trace 按属性更新而非整对象刷新)、限制更少、为跨端演进铺路。混用时注意一套组件不要混用两代局部状态装饰器。
父子与跨层通信
| 场景 | 工具 | 说明 |
|---|---|---|
| 父 → 子传值 | 构造参数 + @Prop(V1)/ @Param(V2) | 只读同步 |
| 子 → 父通知 | 回调函数 / @Link(V1)/ @Event(V2) | 事件上行 |
| 跨多层共享 | @Provide/@Consume(V1)或全局状态 | 就近注入 |
| 全局会话态 | AppStorage / PersistentStorage | 持久化到磁盘 |
| 观察 class 内部 | @Observed + @ObjectLink(V1) | 深观测 |
一个 V2 风格示例:
typescript
@ObservedV2
class Cart {
@Trace total: number = 0
}
@ComponentV2
struct CartBadge {
@Local count: number = 0
cart: Cart = new Cart()
build() {
Text(`购物车 ${this.cart.total}`)
}
}要点:状态只放「会被 UI 读取、需要触发刷新」的数据;列表项、临时局部值不必上装饰器。凡是 UI 依赖的变量修改后要反映到界面,都必须经过状态管理,绕开它去直接改变量不会触发刷新。
网络、异步与并发纪律
- 网络用
@ohos.net.http(http.createHttp())或@kit.NetworkKit的请求能力;原生并发用TaskPool/Worker; - 主线程不阻塞:耗时任务放异步或 TaskPool;UI 线程忙则掉帧;
- 异步安全:装饰器字段更新要回到正确的任务上下文,避免在回调里直接对未标记可共享的对象做脏写;
- 取消场景(组件销毁后仍回包)用
AbortController或组件销毁时断开回调,防止状态泄漏。
常见坑速查
| 坑 | 现象 | 解法 |
|---|---|---|
as any 被拒 | 编译报错 | 显式 interface + 收窄 |
| 对象字面量无法加字段 | 编译报错 | 先声明 class/interface 类型 |
| 改变量不刷新 | UI 不变 | 改用状态装饰器持有 |
| 深对象改了不刷 | 界面不更新 | V1 用 @Observed/@ObjectLink,或升级 V2 @Trace |
| 两代装饰器混用 | 状态行为异常 | 一个组件统一用一代局部装饰器 |
| 回调里 setState 野写入 | 偶发崩溃/数据错乱 | 路由到主线程 + 清理异步句柄 |
检查清单
- [ ] 所有模型有显式 interface/class,无
any逃逸; - [ ] 共享可变状态经状态装饰器管理,无旁路直改;
- [ ] 明确当前用的是 V1 还是 V2,整组件保持一致;
- [ ] 网络/耗时任务不进主线程,取消路径已清理;
- [ ] 未经 DevEco Studio 实机的示例均已在页面标注验证要求。