文章配图:V1 八装饰器复盘、V1 三大局限、V2 六装饰器预览、迁移决策

页面预览

前言

前面 18 篇(31–48)我们把 V1 状态管理体系用了个遍——@State、@Prop、@Link、@Provide/@Consume、@Observed+@ObjectLink、@Watch、@Reusable、LazyForEach。V1 成熟稳定,但有些本质局限:浅观察要整体赋值、@Observed 类必须 new 实例化、跨组件数据流装饰器多易混、批量更新要靠同调用栈。HarmonyOS 推出了 V2 状态管理,用更统一的装饰器和深观察机制解决了这些痛点。

本篇是 V1 收尾总结,对比 V1 与 V2 的核心差异,为下篇 V2 迁移做铺垫。把V1 八装饰器复盘V1 三大局限V2 六装饰器预览迁移决策四大要点讲透。

提示:本系列不讲 ArkTS 基础语法与环境搭建,假设你已跟完第 1–48 篇。本篇是阶段二第十九篇,V1 收尾。

一、V1 八装饰器复盘

1.1 V1 状态管理全家福

装饰器 作用 核心篇
@State 响应式状态,改变触发重渲染 第 31
@Prop 父子单向只读,浅拷贝 第 40
@Link 父子双向,$val 传引用 第 41
@Provide/@Consume 跨层隐式共享,键名匹配 第 42
@Observed+@ObjectLink 类实例深观察,改内部属性触发 第 43
@Watch 状态变化副作用回调 第 39
@Reusable ListItem 复用池,离屏入池 第 47

1.2 装饰器关系图

@State(响应式根基)
  ├─ @Prop(父子单向只读)
  ├─ @Link(父子双向)
  ├─ @Provide/@Consume(跨层隐式)
  ├─ @Observed+@ObjectLink(类实例深观察)
  └─ @Watch(变化副作用)

@Reusable(列表复用,独立)

关键经验V1 以 @State 为根基,其他装饰器要么配合 @State(@Prop/@Link/@Watch/@Observed),要么独立处理列表复用(@Reusable)。

二、V1 三大局限

2.1 局限一:浅观察要整体赋值

@State combo: ComboInfo = { count: 0, multiplier: 1, lastMergeTime: 0 };
@State cats: Cat[] = [];

// ❌ 改内部属性不触发
this.combo.count = 5;
this.cats[0].y = 7;

// ✅ 要整体赋值
this.combo = { ...this.combo, count: 5 };
this.cats = [...this.cats];

痛点:嵌套对象/数组改内部属性要整体赋值,代码冗长且易忘——忘整体赋值就 UI 不刷新,是 V1 最经典 bug。

升级 @Observed 的代价:Cat 要从 interface 改成 @Observed class,必须 new 实例化,子组件必须 @ObjectLink——改动面大

2.2 局限二:@Observed 类必须 new 实例化

@Observed
export class Cat {
  constructor(id: string, level: CatLevel, x: number, y: number, falling: boolean) {
    /* ... */
  }
}

// ✅ 必须 new
const cat = new Cat('cat_0', CatLevel.SMALL, 0, 0, true);

// ❌ 对象字面量不走构造器,不被 @Observed 标记
const cat = { id: 'cat_0', level: CatLevel.SMALL, x: 0, y: 0, falling: true };

痛点:从 interface 改成 class 要写 constructor,所有实例化点从对象字面量改成 new——迁移成本高

2.3 局限三:跨组件数据流装饰器多易混

场景 装饰器 传递语法
显示型只读 @Prop this.score
编辑型双向 @Link this.$gameState
跨层隐式 @Provide/@Consume 键名匹配
类深观察 @ObjectLink 普通值传
变化副作用 @Watch 字符串方法名

痛点:五种装饰器语义接近,初学者易混——@Prop 还是 @Link?@ObjectLink 还是 @Prop?@Watch 字符串拼错不报错。

三、V2 六装饰器预览

3.1 V2 状态管理全家福

装饰器 作用 V1 对应
@Local 本地响应式状态 @State
@Param 父子只读接收 @Prop
@Event 子触发父回调 新(V1 要手写函数 prop)
@Once 单次初始化
@ObservedV2+@Trace 类实例深观察(字段级) @Observed+@ObjectLink
@Monitor 状态变化监听(多字段) @Watch

3.2 V2 的统一简化

// V2:@Local 替代 @State
@Local score: number = 0;

// V2:@Param 替代 @Prop(且深观察自动)
@Param combo: ComboInfo;     // 改 combo.count 也触发!

// V2:@Event 替代手写函数 prop
@Event onScoreAdd: (delta: number) => void;

// V2:@ObservedV2 + @Trace 替代 @Observed + @ObjectLink
@ObservedV2
export class Cat {
  @Trace id: string = '';
  @Trace x: number = 0;
  @Trace y: number = 0;      // 字段级深观察,改 cat.y 触发
}

// V2:@Monitor 替代 @Watch(可监多字段)
@Monitor('combo.count, combo.multiplier')
onComboChange(monitor: Monitor): void {
  /* ... */
}

关键经验V2 用更少装饰器覆盖更多场景——@Param 自动深观察省了 @ObjectLink,@Event 替代手写函数 prop 简化跨组件触发。

3.3 V2 深观察的字段级革命

// V1 @Observed:整类深观察
@Observed
export class Cat {
  id: string;      // 所有字段都深观察
  y: number;
}

// V2 @ObservedV2+@Trace:字段级深观察
@ObservedV2
export class Cat {
  id: string = '';        // 不打 @Trace,不深观察
  @Trace y: number = 0;   // 只 y 深观察
}

关键经验V2 @Trace 字段级深观察——只监听需要响应的字段,省无谓依赖追踪开销。

四、V1 vs V2 核心差异表

4.1 装饰器对照

场景 V1 V2 V2 优势
本地状态 @State @Local 语义更清晰(本地)
父子只读 @Prop(浅拷贝) @Param(深观察) 改内部属性也触发
父子双向 @Link + $val @Param + @Event 分离数据与事件
跨层共享 @Provide/@Consume @Provide/@Consume 保留(V2 也支持)
类深观察 @Observed+@ObjectLink @ObservedV2+@Trace 字段级深观察
变化监听 @Watch(单字段) @Monitor(多字段) 一次监多字段
列表复用 @Reusable @Reusable 保留

4.2 浅观察 vs 深观察

维度 V1 @State/@Prop V2 @Local/@Param
嵌套对象改属性 ❌ 要整体赋值 ✅ 直接触发
数组项改属性 ❌ 要两层赋值 ✅ 直接触发
实例化 interface 字面量 OK class new(V2 也)
代码量 多(整体赋值) 少(改属性即可)

4.3 批量更新

维度 V1 V2
被动批量 ✅ 同调用栈 ✅ 同调用栈
主动批量 ❌ 无(要 setTimeout 套路) batchUpdate API

4.4 类深观察对比

// V1:整类深观察 + 子必须 @ObjectLink
@Observed
export class Cat { id: string; y: number; /* 所有字段 */ }

@Component
struct CatItem {
  @ObjectLink cat: Cat;     // 必须用 @ObjectLink
  build() { /* ... */ }
}

// V2:字段级深观察 + 子用 @Param(自深观察)
@ObservedV2
export class Cat {
  id: string = '';          // 不 @Trace,不深观察
  @Trace y: number = 0;     // 只 y 深观察
}

@Component
struct CatItem {
  @Param cat: Cat;          // 用 @Param,自深观察
  build() { /* ... */ }
}

关键经验V2 用 @Param 替代 @ObjectLink——@Param 自带深观察(接 @ObservedV2 实例),不用单独装饰器。

五、迁移决策

5.1 何时迁 V2

场景 推荐
新项目 ✅ 直接上 V2
老项目小改 ⚠️ 混用 V1 V2(同组件不可混)
老项目大改(状态管理痛点明显) ✅ 全迁 V2
老项目稳定无痛点 ❌ 保持 V1

5.2 混用限制

// ⚠️ 同组件内 V1 V2 不能混
@Component
struct BadMixed {
  @State score: number = 0;        // V1
  @Local combo: ComboInfo = { /* ... */ };   // V2
  // 编译报错或行为未定义
}

关键经验V1 V2 不能在同一组件混用——整个组件要么全 V1 要么全 V2,但不同组件可以分别用 V1 V2。

5.3 迁移工作量评估

迁移项 工作量 收益
@State → @Local 低(改名) 语义清晰
@Prop → @Param 低(改名) 深观察,省整体赋值
@Link → @Param+@Event 中(拆双向) 数据/事件分离
@Observed → @ObservedV2+@Trace 中(字段标 @Trace) 字段级深观察
@ObjectLink → @Param 低(改名) 少一装饰器
@Watch → @Monitor 中(改签名) 多字段监听

关键经验迁移核心收益是「深观察省整体赋值」和「字段级精细监听」——痛点明显的项目迁 V2 值得。

六、猫猫大作战 V1 现状总结

6.1 当前 V1 用到的装饰器

装饰器 用处
@State score、cats、combo、gameState 等所有响应式状态 31–45
@Watch gameState 变化触发音效/震动 39
@Provide/@Consume gameState、score 跨层共享给嵌套子组件 42
@Observed+@ObjectLink Cat 类深观察,CatItem 子组件 43、44
@Reusable RecordItem 列表复用(未来战绩页) 47

6.2 当前 V1 痛点

痛点 当前解法 V2 解法
combo 嵌套对象改属性要整体赋值 {...this.combo, count: 5} this.combo.count = 5 直接触发
cats 数组项改属性要两层赋值 引擎返回新数组整体赋值 this.cats[0].y = 7 直接触发
gameState 变化监听单字段 @Watch(‘onGameStateChange’) @Monitor 可监多字段
PauseOverlay 改父 gameState @Link + $val @Param + @Event 分离

关键经验猫猫大作战当前痛点集中在「嵌套对象/数组整体赋值」——迁 V2 能显著简化。

6.3 迁移优先级建议

优先迁:
  1. @State → @Local(改名简单,收益语义清晰)
  2. @Prop → @Param(改 combo/cats 等嵌套对象为深观察)
  3. @Observed → @ObservedV2+@Trace(Cat 字段级深观察)

可选迁:
  4. @Link → @Param+@Event(PauseOverlay 等编辑型组件)
  5. @Watch → @Monitor(gameState 监听)

保留:
  6. @Provide/@Consume(V2 也支持,不改)
  7. @Reusable(V2 也支持,不改)

七、踩坑提示

7.1 V1 V2 同组件混用

// ❌ 错误:同组件混用,编译报错
@Component
struct Bad {
  @State score: number = 0;      // V1
  @Local combo: ComboInfo = { /* ... */ };   // V2
}

// ✅ 正确:整个组件统一 V1 或 V2
@Component
struct GoodV1 {
  @State score: number = 0;
  @State combo: ComboInfo = { /* ... */ };
}

@Component
struct GoodV2 {
  @Local score: number = 0;
  @Local combo: ComboInfo = { /* ... */ };
}
``

### 7.2 以为 V2 全替代 V1

```ts
// ⚠️ V2 不是全替代,有些 V1 装饰器 V2 也支持
@Provide/@Consume     // V1 V2 都有,不变
@Reusable             // V1 V2 都有,不变

实战经验V2 是 V1 的进化,不是替代——@Provide/@Consume/@Reusable 保持,@State/@Prop/@Link/@Observed/@Watch 升级。

7.3 迁移忘改实例化

// V1 @Observed class
@Observed
export class Cat { /* ... */ }
const cat = new Cat(/* ... */);

// V2 @ObservedV2 class
@ObservedV2
export class Cat {
  @Trace y: number = 0;
}
// 还是 new 实例化,不变
const cat = new Cat(/* ... */);

// ⚠️ 但如果 V1 用 interface 对象字面量,迁 V2 要改成 class new
``

## 八、调试技巧

1. **DevEco 版本支持**:确认 DevEco Studio 和 SDK 支持 V2API 12+)。
2. **`console.info` 对比 V1 V2 行为**:迁后改属性看是否触发(V2 应触发)。
3. **混用排查**:编译报「V1 V2 不能混用」,检查组件内装饰器。
4. **@Trace 忘标**:迁 @ObservedV2 后忘给字段标 @Trace,该字段不深观察。

## 九、性能与最佳实践

1. **新项目直接上 V2**——深观察省整体赋值,字段级精细监听。
2. **老项目痛点明显才迁**——稳定无痛点保持 V1,迁要全组件迁。
3. **V1 V2 不能同组件混用**——整个组件统一选一版。
4. **@Provide/@Consume/@Reusable V1 V2 都支持**——迁移时这两个保留。
5. **迁移核心收益是深观察**——嵌套对象/数组改属性直接触发,省整体赋值。
6. **@Monitor 多字段监听**——一次监听多个字段变化,比 @Watch 灵活。

## 十、阶段二进度小结(3149)

本篇是阶段二「状态管理 + 交互 + 动画」第 19 篇,V1 收尾:

|| 主题 | 核心要点 |
|----|------|----------|
| 31 | @State | 响应式状态 |
| 32 |@State | 同帧批量 |
| 33 | setInterval 主循环 | 100ms 物理周期 |
| 34 | 计时器 | 1000ms 秒级 |
| 35 | spawnTimer | 2000ms 自动生成 |
| 36 | clearTimers | -1 哨兵,三时机清理 |
| 37 | onClick 列投放 | 闭包捕获 col |
| 38 | 箭头函数 this | 回调统一箭头 |
| 39 | @Watch | 状态变化副作用 |
| 40 | @Prop | 父子单向只读 |
| 41 | @Link | 父子双向 $val |
| 42 | @Provide/@Consume | 跨层隐式共享 |
| 43 | @Observed+@ObjectLink | 类实例深观察 |
| 44 | 数组项替换 | ForEach 密钥 diff |
| 45 | 批量更新 | 被动批量 + batchUpdate |
| 46 | 嵌套陷阱 | 浅观察整体赋值 |
| 47 | @Reusable | 列表复用池 |
| 48 | LazyForEach | 大列表按需渲染 |
| 49(本篇) | V1 局限总结 | V1 V2 差异 |

接下来第 50 篇会专讲 V2 迁移——@Local/@Param/@Event/@Trace/@Monitor 的实战。

## 总结

本篇我们做 V1 收尾总结,掌握了**V1 八装饰器复盘****V1 三大局限(浅观察、@Observed 必须 new、装饰器多易混)****V2 六装饰器预览****迁移决策(新项目直接 V2,老项目痛点明显才迁)**四大要点。核心要点:**V1 浅观察要整体赋值是最大痛点;V2 @Param 自深观察省整体赋值;@Trace 字段级精细监听;V1 V2 不能同组件混用**。

下一篇我们将专讲 V2 迁移实战——@Local/@Param/@Event/@ObservedV2+@Trace/@Monitor 的改造。

> 如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!

---

**相关资源:**
- 「猫猫大作战」项目源码:本仓库 `entry/src/main/ets/pages/Index.ets``entry/src/main/ets/components/`
- [ArkUI V2 状态管理官方指南](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-state-management-v2)
- [ArkUI V1 V2 差异对比官方文档](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-state-management-overview)
- [ArkUI 状态管理迁移最佳实践](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/bpta-state-migration)
- [开源鸿蒙跨平台社区](https://openharmonycrossplatform.csdn.net)
- [HarmonyOS 开发者官方文档首页](https://developer.huawei.com/consumer/cn/doc/)
- 系列索引:本仓库 `articles/INDEX.md`
Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐