Appearance
状态管理 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 特性:
├── 运行时创建,无需编译期装饰器
├── 支持动态数据(运行时才知道的数据结构)
├── 可以拦截和自定义属性访问行为
├── 深度代理,自动处理嵌套对象
└── 性能开销略高于 @Trace5.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 的区别。回答时强调性能优化优势。