总览表
| 装饰器 | 作用 | 使用位置 |
|---|---|---|
@ComponentV2 |
声明 V2 自定义组件 | struct |
@ObservedV2 |
标记类为可深度观察 | class |
@Trace |
标记属性参与精确刷新 | @ObservedV2 类的属性 1 |
@Local |
组件内部响应式状态 | @ComponentV2 组件内 |
@Param |
父→子单向输入 | @ComponentV2 组件内 |
@Once |
仅首次初始化同步 | 配合 @Param 使用 |
@Require |
强制父组件必传 | 配合 @Param 使用 |
@Event |
子→父事件输出 | @ComponentV2 组件内 |
@Provider / @Consumer |
跨层级状态共享 | @ComponentV2 组件内 |
@Monitor |
状态变化监听 | 组件内 / @ObservedV2 类内 2 |
@Computed |
缓存派生计算属性 | 组件内 / @ObservedV2 类内 2 |
@Type |
标记类属性的运行时类型 | @ObservedV2 类属性 1 |
@ReusableV2 |
V2 版本组件复用 | @ComponentV2 组件 |
逐一详解
1. @ComponentV2 ------ V2 组件声明
V1 用 @Component,V2 用 @ComponentV2。两者不能混用 ,@ComponentV2 组件内只能用 V2 装饰器。34
typescript
@ComponentV2
struct MyComponent {
build() {
Column() {
Text('这是 V2 组件')
}
}
}
2. @ObservedV2 + @Trace ------ 深度可观察数据模型
V1 的 @Observed 只能观察一级属性变化;V2 的 @ObservedV2 配合 @Trace 实现属性级别的深度精确刷新。1
typescript
@ObservedV2
class Address {
@Trace city: string = '北京'
@Trace zipCode: string = '100000'
}
@ObservedV2
class Person {
@Trace name: string = '张三'
@Trace age: number = 25
@Trace address: Address = new Address() // 嵌套对象也能被追踪
}
关键规则:
@ObservedV2单独使用无效 ,必须搭配@Trace1@Trace只能出现在@ObservedV2修饰的类中12- 修改
@Trace属性时,只有该属性对应的 UI 被刷新,而非整个组件重绘
3. @Local ------ 组件内部状态(替代 V1 的 @State)
typescript
@ComponentV2
struct Counter {
@Local count: number = 0 // 简单类型
@Local user: Person = new Person() // 引用类型(配合 @ObservedV2)
build() {
Column() {
Text(`计数: ${this.count}`)
Button('+1').onClick(() => {
this.count++ // 触发 UI 刷新
})
}
}
}
与 @State 的区别 :@Local 只能局部初始化,不能从外部传入,语义更明确。
4. @Param ------ 父→子单向输入(替代 V1 的 @Prop)
typescript
@ComponentV2
struct ChildView {
@Param title: string = '' // 可选,有默认值
@Require @Param user: Person // 必传,配合 @Require
@Param count: number = 0
build() {
Column() {
Text(this.title)
Text(`${this.user.name}, ${this.user.age}`)
Text(`count: ${this.count}`)
}
}
}
// 父组件使用
@Entry
@ComponentV2
struct ParentView {
@Local person: Person = new Person()
build() {
ChildView({
title: '用户信息', // 传入
user: this.person, // 必传
// count 可省略,使用默认值 0
})
}
}
注意 :@Param 在子组件内只读 ,不能直接修改其本身,但可以修改其 @Trace 属性(如 this.user.name)。
5. @Once ------ 仅初始化同步一次
配合 @Param 使用,初始化后父组件变更不再同步到子组件。
typescript
@ComponentV2
struct PriorityBadge {
@Once @Param priority: number = 0 // 创建时确定,后续不再更新
build() {
Text(this.getLabel())
.fontColor(this.getColor())
}
getLabel(): string {
return ['低', '中', '高'][this.priority]
}
getColor(): string {
return ['#27AE60', '#F39C12', '#E74C3C'][this.priority]
}
}
适用场景:创建后不会改变的"快照"数据,如优先级标签、创建时间等。
6. @Require ------ 强制父组件必传
typescript
@ComponentV2
struct UserCard {
@Require @Param name: string // 父组件必须传,没有默认值
@Param age: number = 0 // 可选
build() {
Row() {
Text(this.name)
Text(`${this.age}岁`)
}
}
}
// 使用
UserCard({ name: '李四', age: 30 }) // ✅ 必须传 name
// UserCard({}) // ❌ 编译报错:name 必须传
7. @Event ------ 子→父事件输出(替代 V1 的 @Link)
V2 取消了隐式双向绑定,子→父通信必须通过 @Event 显式回调。
typescript
@ComponentV2
struct TaskCard {
@Require @Param task: TaskItem
@Event onToggleComplete: (id: string) => void // 事件回调
@Event onDelete: (id: string) => void
build() {
Row() {
Checkbox()
.select(this.task.completed)
.onChange(() => {
this.onToggleComplete(this.task.id) // 通知父组件
})
Text(this.task.title).layoutWeight(1)
Button('删除').onClick(() => {
this.onDelete(this.task.id)
})
}
}
}
// 父组件
@Entry
@ComponentV2
struct TaskList {
@Local tasks: TaskItem[] = []
build() {
ForEach(this.tasks, (task: TaskItem) => {
TaskCard({
task: task,
onToggleComplete: (id: string) => {
// 父组件负责修改数据,然后通过 @Param 同步回子组件
const t = this.tasks.find(t => t.id === id)
if (t) t.completed = !t.completed
},
onDelete: (id: string) => {
this.tasks = this.tasks.filter(t => t.id !== id)
}
})
})
}
}
数据流向 :父→子(@Param) + 子→父(@Event) = 显式单向数据流。
8. @Provider / @Consumer ------ 跨层级共享(替代 V1 的 @Provide / @Consume)
无需逐层 @Param 传递,祖先发布、后代自动订阅。
typescript
@ObservedV2
class AppConfig {
@Trace theme: string = 'light'
@Trace fontSize: number = 14
}
@Entry
@ComponentV2
struct MainPage {
@Provider() config: AppConfig = new AppConfig() // 发布
build() {
Column() {
Text('主页')
MiddleComponent() // ← 中间组件无需显式传参
}
}
}
@ComponentV2
struct MiddleComponent {
build() {
Column() {
Text('中间组件')
DeepChild() // ← 也没有显式传参
}
}
}
@ComponentV2
struct DeepChild {
@Consumer() config: AppConfig = new AppConfig() // 自动订阅
build() {
Column() {
Text(`当前主题: ${this.config.theme}`)
Text(`字体大小: ${this.config.fontSize}`)
}
}
}
关键规则 :默认按变量名(config)匹配 @Provider 和 @Consumer,双向同步。
9. @Monitor ------ 状态变化监听(替代 V1 的 @Watch)
支持深层路径监听 ,这是 V1 的 @Watch 做不到的。
typescript
@ObservedV2
class TaskItem {
@Trace id: string = ''
@Trace title: string = ''
@Trace completed: boolean = false
}
@ComponentV2
struct TaskCard {
@Require @Param task: TaskItem
// 监听深层属性变化,可获取 before / now
@Monitor('task.completed')
onCompletedChange(monitor: IMonitor) {
const before = monitor.value()?.before
const now = monitor.value()?.now
console.info(`任务状态变更: ${before} → ${now}`)
}
// 同时监听多个属性
@Monitor('task.title', 'task.completed')
onAnyChange(monitor: IMonitor) {
monitor.dirty.forEach((path: string) => {
console.info(`${path} 发生了变化`)
})
}
build() {
Row() {
Checkbox().select(this.task.completed)
Text(this.task.title)
}
}
}
也可以写在数据类内部:
typescript
@ObservedV2
class Player {
@Trace hp: number = 100
@Trace mp: number = 50
@Monitor('hp')
onHpChange(monitor: IMonitor) {
const before = monitor.value()?.before as number
const now = monitor.value()?.now as number
if (now <= 0) {
console.info('玩家死亡')
}
}
}
10. @Computed ------ 缓存派生计算属性
仅在依赖的 @Trace 属性变化时重新计算,避免 build() 中重复运算。
typescript
@ObservedV2
class TaskStats {
@Trace total: number = 0
@Trace completedCount: number = 0
@Computed
get pendingCount(): number {
return this.total - this.completedCount // 仅在 total 或 completedCount 变化时重新计算
}
@Computed
get completionRate(): string {
if (this.total === 0) return '0%'
return `${Math.round((this.completedCount / this.total) * 100)}%`
}
}
组件内也可以使用:
typescript
@ComponentV2
struct UserInfo {
@Local firstName: string = ''
@Local lastName: string = ''
@Computed
get fullName(): string {
return `${this.lastName} ${this.firstName}` // 缓存,不重复计算
}
build() {
Column() {
Text(this.fullName) // 多次引用也只算一次
Text(this.fullName)
}
}
}
11. @Type ------ 标记类属性的运行时类型
用于声明 @Trace 属性的具体类型,辅助运行时类型推断(适用于集合类型等场景)。
typescript
@ObservedV2
class DataModel {
@Trace items: Array<string> = []
// 标记类型
@Type(Array<string>)
@Trace tags: string[] = []
@Type(Map<string, number>)
@Trace scoreMap: Map<string, number> = new Map()
}
12. @ReusableV2 ------ V2 版本组件复用
V1 的 @Reusable 对应 V2 的 @ReusableV2,用于长列表中的组件复用优化。
typescript
@ReusableV2
@ComponentV2
struct ReusableListItem {
@Param item: string = ''
@Param index: number = 0
aboutToReuse(params: Record<string, Object>): void {
// 复用前更新数据
this.item = params.item as string
this.index = params.index as number
}
build() {
Row() {
Text(`${this.index}: ${this.item}`)
}
}
}
V1 → V2 对照速查
| V1 装饰器 25 | V2 装饰器 2 | 核心变化 |
|---|---|---|
@Component |
@ComponentV2 |
组件声明 |
@State |
@Local |
不能外部传入,语义更纯 |
@Prop |
@Param |
单向输入 |
@Link |
@Param + @Event |
双向绑定拆为显式单向+事件 |
@Watch |
@Monitor |
支持深层路径监听、获取 before/now |
@Provide / @Consume |
@Provider / @Consumer |
按变量名自动匹配 |
@Observed + @ObjectLink |
@ObservedV2 + @Trace |
属性级精确刷新,支持深层嵌套 |
| 无 | @Computed |
V2 新增:缓存派生值 |
| 无 | @Once |
V2 新增:一次性初始化 |
| 无 | @Require |
V2 新增:强制必传参数 |
@Reusable |
@ReusableV2 |
V2 版本复用 |
核心设计理念
less
┌──────────────────────────┐
│ @Provider / @Consumer │ ← 跨层级共享
│ (跨组件全局状态) │
└──────────┬───────────────┘
│
┌──────────────────────┼──────────────────────┐
▼ ▼ ▼
@Local @Param ←──→ @Event @Computed
(组件内部状态) (父→子输入) (子→父输出) (派生计算)
│ │
└──────────┬───────────┘
▼
@ObservedV2 + @Trace
(数据模型层,属性级精确刷新)
│
▼
@Monitor
(变化监听 + 副作用)
V2 的核心理念:单向数据流 + 显式事件回调,取消隐式双向绑定,数据流向清晰可追踪,状态变化可精确到单个属性级别进行刷新。