Skip to content

状态管理 V2 深度

1. 状态管理 V2 概述

1.1 V1 vs V2 对比

状态管理版本演进:

ArkUI State Management V1:
├── @State / @Prop / @Link / @Provide / @Consume
├── @Observed / @ObjectLink
├── AppStorage / LocalStorage / PersistentStorage
└── 局限:响应式粒度粗、嵌套对象监听不完美

ArkUI State Management V2:
├── @Trace V2 — 细粒度响应式追踪
├── @Local / @Param — 新的数据绑定方式
├── @Event — 响应式回调
├── ObservedV2 — 深度对象监听
├── Signal 响应式 — 细粒度信号
├── Proxy 代理 — 动态数据管理
└── 优势:响应式粒度精确、性能更优、开发体验更好

1.2 V2 核心设计理念

V2 设计目标:
├── 细粒度响应:只更新真正依赖变化的组件
├── 类型安全:完整的 TypeScript 类型推导
├── 性能优化:减少不必要的渲染
├── 简洁语法:减少样板代码
└── 向后兼容:与 V1 状态共存

2. @Trace 深度解析

2.1 @Trace 原理

@Trace 是 V2 的核心装饰器,实现细粒度的响应式追踪:

┌──────────────────────────────────────────────────────┐
│  @Trace 工作原理                                    │
│                                                      │
│  1. 编译期:Dawn 编译器将 @Trace 标记的类/属性       │
│     编译为响应式类型                                  │
│                                                      │
│  2. 运行期:Proxy 代理拦截属性的 get/set             │
│     → get: 建立依赖关系(依赖收集)                   │
│     → set: 触发依赖更新(变更通知)                   │
│                                                      │
│  3. 更新机制:                                       │
│     属性变化 → 追踪依赖 → 标记脏节点 → 局部刷新       │
└──────────────────────────────────────────────────────┘

2.2 @Trace 使用

arkts
// 1. 定义响应式类
import { Trace } from '@kit.ArkUI';

@Trace
class UserModel {
  @Trace name: string = 'John';
  @Trace age: number = 25;
  @Trace address: Address = new Address();

  constructor() {
    // 响应式属性自动代理
  }

  // 响应式方法(方法调用不会触发刷新,需要返回响应式值)
  updateName(newName: string) {
    this.name = newName;
  }
}

@Trace
class Address {
  @Trace city: string = 'Beijing';
  @Trace street: string = 'ChangAn';
}

// 2. 使用响应式数据
@Entry
@Component
struct UserCard {
  @Trace user: UserModel = new UserModel();

  build() {
    Column() {
      Text(`Name: ${this.user.name}`)
        .fontSize(18)
      Text(`Age: ${this.user.age}`)
        .fontSize(16)
      Text(`City: ${this.user.address.city}`)
        .fontSize(14)

      Button('Update Name')
        .onClick(() => {
          this.user.name = 'Jane';  // ✅ 只刷新相关 Text
          this.user.address.city = 'Shanghai';  // ✅ 只刷新 Address Text
        })
    }
  }
}

2.3 @Trace 与 @State 对比

特性@State (V1)@Trace (V2)
响应粒度粗(整个组件刷新)细(精确到属性)
嵌套对象需要 @Observed + @ObjectLink自动响应
性能中等更优
语法属性装饰器类装饰器 + 属性装饰器
类型推导有限完整
适用场景简单状态复杂对象/类

3. ObservedV2 深度对象监听

3.1 ObservedV2 原理

ObservedV2 是 V2 的深层对象监听机制:

┌──────────────────────────────────────────────────────┐
│  ObservedV2 机制                                     │
│                                                      │
│  1. 递归代理:对整个对象树建立 Proxy                 │
│  2. 惰性计算:只在读取属性时建立依赖                  │
│  3. 精确追踪:只标记直接依赖的属性                    │
│  4. 自动清理:组件销毁时自动断开依赖                  │
└──────────────────────────────────────────────────────┘

3.2 ObservedV2 使用

arkts
import { ObservedV2 } from '@kit.ArkUI';

// 1. 定义嵌套对象
@ObservedV2
class Order {
  @Trace items: OrderItem[] = [];
  @Trace totalAmount: number = 0;
  @Trace status: string = 'pending';
}

@ObservedV2
class OrderItem {
  @Trace name: string = '';
  @Trace price: number = 0;
  @Trace quantity: number = 1;
}

// 2. 使用
@Entry
@Component
struct OrderList {
  @Trace order: Order = new Order();

  aboutToAppear() {
    this.order.items = [
      { name: 'Apple', price: 10, quantity: 2 },
      { name: 'Banana', price: 5, quantity: 3 }
    ];
    this.order.totalAmount = 35;
  }

  build() {
    Column() {
      // 自动响应嵌套属性变化
      ForEach(this.order.items, (item: OrderItem) => {
        Text(`${item.name} x${item.quantity} = ¥${item.price * item.quantity}`)
      })

      Text(`Total: ¥${this.order.totalAmount}`)

      Button('Add Item')
        .onClick(() => {
          this.order.items.push({
            name: 'Orange',
            price: 8,
            quantity: 1
          });
          this.order.totalAmount += 8;  // 自动触发刷新
        })
    }
  }
}

3.3 ObservedV2 vs 旧版 Observed

特性@Observed (V1)@ObservedV2 (V2)
嵌套监听需 @ObjectLink 配合自动递归代理
数组操作需手动 notify自动监听 push/pop
性能较深时开销大惰性计算,更优
类型推导有限完整
迁移成本需重写渐进式迁移

4. Signal 响应式

4.1 Signal 原理

Signal 是 V2 引入的细粒度响应式机制:

Signal 概念:
├── Signal 是一个包含 value 和 dependents 的对象
├── 读取 signal.value 时建立依赖
├── 设置 signal.value 时通知依赖
├── 依赖更新时自动执行计算(computed)
└── 支持批量更新(transaction)

Signal 与 @Trace 的关系:
├── @Trace 是声明式,编译期生成
├── Signal 是编程式,运行期创建
├── Signal 更灵活(可以条件创建/动态销毁)
└── @Trace 更简洁(代码量少)

4.2 Signal 使用

arkts
import { Signal, computed, batch } from '@kit.ArkUI';

// 1. 创建 Signal
class Counter {
  count: Signal<number> = new Signal(0);
  name: Signal<string> = new Signal('Counter');

  increment() {
    this.count.value += 1;
  }
}

// 2. 创建派生信号(自动追踪依赖)
class ComputedValues {
  private _count: Signal<number> = new Signal(0);
  private _multiplier: Signal<number> = new Signal(2);

  // 派生值:当 count 或 multiplier 变化时自动重新计算
  get doubled() {
    return this._count.value * this._multiplier.value;
  }

  set count(v: number) { this._count.value = v; }
  get count() { return this._count.value; }

  set multiplier(v: number) { this._multiplier.value = v; }
  get multiplier() { return this._multiplier.value; }
}

// 3. 批量更新(合并多次变更通知)
function batchUpdate() {
  batch(() => {
    // 这 3 次修改只会触发一次刷新
    count.value = 1;
    name.value = 'New';
    total.value = 100;
  });
}

5. Proxy 代理式数据管理

5.1 Proxy 原理

Proxy 是 V2 的运行时数据代理机制:

Proxy 特性:
├── 运行时创建,无需编译期装饰器
├── 支持动态数据(运行时才知道的数据结构)
├── 可以拦截和自定义属性访问行为
├── 深度代理,自动处理嵌套对象
└── 性能开销略高于 @Trace

5.2 Proxy 使用

arkts
import { Proxy } from '@kit.ArkUI';

// 1. 创建响应式代理
function createDynamicConfig(): Proxy<Config> {
  const raw: Config = {
    theme: 'light',
    fontSize: 16,
    items: []
  };
  return Proxy.create(raw);
}

interface Config {
  theme: string;
  fontSize: number;
  items: string[];
}

// 2. 使用代理
@Entry
@Component
struct DynamicConfigPage {
  @Trace config: Proxy<Config> = createDynamicConfig();

  build() {
    Column() {
      Text(`Theme: ${this.config.theme}`)
      Text(`Font Size: ${this.config.fontSize}`)

      Button('Switch Theme')
        .onClick(() => {
          this.config.theme = this.config.theme === 'light' ? 'dark' : 'light';
        })
    }
  }
}

5.3 @Param 与 @Event

arkts
// @Param:父组件向子组件传入响应式参数
@Component
struct ChildComponent {
  @Param value: number;  // 响应式绑定,子组件可修改

  build() {
    Text(`Value: ${this.value}`)
  }
}

// @Event:子组件向父组件传递回调
@Component
struct ParentComponent {
  @Param childValue: number = 0;

  build() {
    ChildComponent({
      value: this.childValue,
      onChange: (newValue: number) => {
        this.childValue = newValue;  // 自动刷新
      }
    })
  }
}

6. 状态管理混合方案

6.1 推荐方案

状态管理选型指南:

┌──────────────────────┬───────────────┬────────────────┐
│ 场景                  │ 推荐方案       │ 原因            │
├──────────────────────┼───────────────┼────────────────┤
│ 组件内部状态          │ @State        │ 简单直接        │
│ 父子单向数据流        │ @Prop         │ 单向拷贝安全    │
│ 父子双向绑定          │ @Link         │ 双向同步        │
│ 复杂嵌套对象          │ @Trace + @ObservedV2 │ 细粒度+深度监听  │
│ 运行时动态对象        │ Proxy         │ 动态创建        │
│ 简单计算值            │ Signal        │ 细粒度响应      │
│ 全局单例              │ AppStorage    │ 应用级共享       │
│ 页面级存储            │ LocalStorage  │ 页面/Ability 隔离 │
└──────────────────────┴───────────────┴────────────────┘

6.2 实际项目架构

推荐架构(大型项目):
┌─────────────────────────────────────────────────────┐
│  Store(全局状态,使用 @Trace 类)                   │
│  ├── @Trace userStore                              │
│  ├── @Trace settingsStore                          │
│  └── @Trace cartStore                              │
├─────────────────────────────────────────────────────┤
│  Service(业务逻辑层)                              │
│  ├── UserService                                   │
│  ├── SettingsService                               │
│  └── CartService                                   │
├─────────────────────────────────────────────────────┤
│  Component(UI 组件)                               │
│  ├── 子组件用 @Param 接收状态                       │
│  ├── 子组件用 @Event 通知父组件                     │
│  └── 局部状态用 @State                            │
└─────────────────────────────────────────────────────┘

7. 🎯 面试高频考点

Q1: @Trace V2 与 @State V1 的区别?

答要点

  • 响应粒度:@Trace 细粒度(属性级),@State 粗粒度(组件级)
  • 嵌套对象:@Trace 自动响应嵌套变化,@State 需要 @Observed + @ObjectLink
  • 性能:@Trace 只更新依赖的子树,@State 刷新整个组件
  • 语法:@Trace 是类装饰器 + 属性装饰器,@State 是属性装饰器
  • 适用:@Trace 适合复杂对象,@State 适合简单状态

Q2: ObservedV2 的原理是什么?

答要点

  • 递归代理整个对象树
  • 读取时建立依赖关系(依赖收集)
  • 写入时触发依赖更新(变更通知)
  • 惰性计算,只在真正使用时建立依赖
  • 组件销毁时自动清理依赖
  • 自动监听数组操作(push/pop/splice)

Q3: Signal 与 @Trace 怎么选?

答要点

  • @Trace:声明式,编译期生成,代码简洁,适合大多数场景
  • Signal:编程式,运行时创建,更灵活,适合动态创建/条件响应
  • Signal 支持批量更新(transaction),@Trace 不支持
  • @Trace 性能更优(编译期优化),Signal 开销略大
  • 新项目优先使用 @Trace,复杂场景用 Signal

💡 面试提示:状态管理 V2 是面试最高频考点之一。重点掌握 @Trace 细粒度响应原理ObservedV2 递归代理Signal 与 @Trace 的区别。回答时强调性能优化优势。