本文是「ArkTS 进阶之道」系列第 30 篇,续「ArkUI 状态联动」深水区。上篇讲环境变量状态边界(Environment 设备环境查询只读不可写单向链)。本文讲精准观测边界:@Track class 属性级精准观测 ------根因在只刷关联 UI 避免冗余刷新。能力系列篇 30 讲过 @Track 怎么用,本文讲为哈 @Track 精准观测合法只刷关联 UI 不整体刷新------根因在属性级观测槽。
一、开篇:@Track 不是整体刷新,是 class 属性级精准观测
你写 React 时,精准观测是「依赖数组魔法」(useEffect 依赖数组控制触发范围):
typescript
// React 精准观测:useEffect 依赖数组魔法
function useTrackExample() {
const [str1, setStr1] = useState('str1-init')
const [str2, setStr2] = useState('str2-init')
// ✅ 依赖数组 [str1]:只 str1 变才触发
useEffect(() => { console.log('str1 changed') }, [str1])
// ✅ 依赖数组 [str2]:只 str2 变才触发
useEffect(() => { console.log('str2 changed') }, [str2])
// ⚠ 依赖数组 [str1, str2]:任一变都触发(冗余触发)
}
// React 精准观测:useEffect 依赖数组魔法(只刷关联副作用)
你写鸿蒙 ArkTS 时,@Track class 属性级精准观测------只刷关联 UI 避免冗余刷新:
typescript
// ArkTS @Track class 属性级精准观测
@Observed
class TrackClass {
@Track str1: string = 'str1-init' // ✅ @Track 装饰,变化时只刷 str1 关联 UI
@Track str2: string = 'str2-init' // ✅ @Track 装饰,变化时只刷 str2 关联 UI
}
@Entry
@Component
struct Index {
@State trackObj: TrackClass = new TrackClass()
build() {
Column() {
Text(`${this.trackObj.str1}`) // ✅ str1 变只刷这一行
Text(`${this.trackObj.str2}`) // ✅ str2 变只刷这一行
Button('改 str1').onClick(() => { this.trackObj.str1 = 'changed' })
}
}
}
// @Track class 属性级精准观测:只刷关联 UI,避免冗余刷新
useEffect 依赖数组魔法 vs 属性级精准观测的区别:React 把精准观测当 useEffect 依赖数组魔法(依赖数组控制触发范围),ArkTS 把 @Track 当「属性级精准观测」(@Track 装饰属性变化只刷关联 UI)。根因不是 useEffect 依赖数组魔法是属性级精准观测------@Track class 属性级精准观测只刷关联 UI,避免冗余刷新。
二、根因:@Track 的属性级精准观测机制
鸿蒙 ArkUI 的 @Track 是属性级精准观测绑定------@Track 装饰属性变化只刷关联 UI,来自三重绑定机制。
机制 1:@Track class 属性级观测------不是整体刷新
@Track 荬饰器class 属性级观测------@Track 装饰的属性变化只触发该属性关联的 UI 更新:
typescript
// ✅ @Observed + @Track:class 属性级精准观测
@Observed
class TrackClass {
@Track str1: string = 'str1-init' // ✅ @Track 装饰,变化时只刷 str1 关联 UI
@Track str2: string = 'str2-init' // ✅ @Track 装饰,变化时只刷 str2 关联 UI
}
// ⚠ 对比类:不用 @Track(V1 默认行为,整体刷新)
@Observed
class NoTrackClass {
str1: string = 'str1-init' // ⚠ 未装饰 @Track,变化时触发整个 class 关联 UI 刷新
str2: string = 'str2-init' // ⚠ 未装饰 @Track
}
@Track 装饰属性 vs 未装饰属性的区别:@Track 装饰属性(str1)变化只刷 str1 关联 UI,未装饰属性(str1)变化触发整个 class 关联 UI 都刷(冗余刷新)。根因不是整体刷新是属性级观测------@Track class 属性级观测只刷关联 UI,避免冗余刷新。
机制 2:@Track 只刷关联 UI------避免冗余刷新
@Track 荬饰器只刷关联 UI------@Track 装饰属性变化只刷该属性关联的 UI,其他 UI 不刷:
typescript
// ✅ @Track 只刷关联 UI(避免冗余刷新)
@Entry
@Component
struct Index {
@State trackObj: TrackClass = new TrackClass()
build() {
Column() {
// ✅ str1 变只刷这一行,str2 行不刷
Text(`str1 = ${this.trackObj.str1}`)
// ✅ str2 变只刷这一行,str1 行不刷
Text(`str2 = ${this.trackObj.str2}`)
Button('改 str1').onClick(() => {
this.trackObj.str1 = 'str1-changed' // ✅ 只刷 str1 行,str2 行不刷
})
}
}
}
// @Track 只刷关联 UI:str1 变只刷 str1 行,str2 行不刷(避免冗余刷新)
@Track 只刷关联 UI vs 不用 @Track 整体刷新的区别:@Track 只刷关联 UI(str1 变只刷 str1 行,str2 行不刷),不用 @Track 整体刷新(str1 变整个 class 关联 UI 都刷,str2 行也刷冗余)。根因不是整体刷新是属性级观测------@Track 只刷关联 UI,避免冗余刷新。
机制 3:@Track 限制条件------非 @Track 属性不能在 UI 中使用
@Track 荬饰器限制条件------如果 class 类中使用了 @Track 装饰器,则未被 @Track 装饰的属性不能在 UI 中使用:
typescript
// ⚠ @Track 限制条件:非 @Track 属性不能在 UI 中使用
@Observed
class TrackClass {
@Track str1: string = 'str1-init' // ✅ @Track 装饰
str2: string = 'str2-init' // ⚠ 未装饰 @Track,不能在 UI 中使用
}
@Entry
@Component
struct Index {
@State trackObj: TrackClass = new TrackClass()
build() {
Column() {
// ✅ str1 被 @Track 装饰,可以在 UI 中使用
Text(`str1 = ${this.trackObj.str1}`)
// ⚠ str2 未被 @Track 装饰,在 UI 中使用会运行时报错
// Text(`str2 = ${this.trackObj.str2}`) // ❌ 运行时报错
}
}
}
// @Track 限制条件:class 中用了 @Track,非 @Track 属性不能在 UI 中使用(运行时报错)
@Track 限制条件 vs 未装饰属性可用的区别:@Track 限制条件(class 中用了 @Track,非 @Track 属性不能在 UI 中使用,运行时报错),未装饰属性可用(class 中没用 @Track,所有属性都能在 UI 中使用)。根因不是未装饰属性可用是 @Track 限制条件------@Track 限制条件非 @Track 属性不能在 UI 中使用,运行时报错。
三、真机配图:@Track 精准观测边界------只刷关联 UI

真机配图展示 @Track class 属性级精准观测只刷关联 UI:
- @Track 精准观测区(只刷关联 UI):trackObj.str1 = str1-init / trackObj.str2 = str2-init,改 str1 只刷 str1 行,str2 行不刷
- 不用 @Track 对比区(整体冗余刷新):noTrackObj.str1 = str1-init / noTrackObj.str2 = str2-init,改 str1 整个 class 关联 UI 都刷
- 验证按钮区:改 trackObj.str1 = "str1-changed"(@Track 只刷 str1 行,str2 行不刷)/ 改 noTrackObj.str1 = "str1-changed"(不用 @Track,整个 class 关联 UI 都刷)
四、真解法:@Track 精准观测的三个场景
场景 1:@Track 避免冗余刷新(90% 场景首选,class 属性级精准观测)
@Track 避免冗余刷新用 @Observed + @Track 装饰 class 属性:
typescript
// ✅ 场景 1:@Track 避免冗余刷新(class 属性级精准观测)
@Observed
class LogModel {
@Track level: string = 'INFO' // ✅ @Track 装饰,变化时只刷 level 关联 UI
@Track message: string = 'init' // ✅ @Track 装饰,变化时只刷 message 关联 UI
@Track timestamp: number = 0 // ✅ @Track 装饰,变化时只刷 timestamp 关联 UI
}
@Entry
@Component
struct Index {
@State log: LogModel = new LogModel()
build() {
Column() {
Text(`level = ${this.log.level}`) // ✅ level 变只刷这一行
Text(`message = ${this.log.message}`) // ✅ message 变只刷这一行
Text(`timestamp = ${this.log.timestamp}`) // ✅ timestamp 变只刷这一行
Button('改 level').onClick(() => { this.log.level = 'ERROR' })
}
}
}
// @Track 避免冗余刷新:level 变只刷 level 行,message/timestamp 行不刷(避免冗余刷新)
场景 2:@Track 与 @Observed 配合(必须 @Observed 装饰 class)
@Track 与 @Observed 配合------@Track 必须在 @Observed 装饰的 class 中使用:
typescript
// ✅ 场景 2:@Track 与 @Observed 配合(必须 @Observed 装饰 class)
@Observed // ✅ @Observed 装饰 class
class UserModel {
@Track name: string = 'HarmonyOS' // ✅ @Track 装饰属性
@Track age: number = 18 // ✅ @Track 装饰属性
}
@Entry
@Component
struct Index {
@State user: UserModel = new UserModel()
build() {
Column() {
Text(`name = ${this.user.name}`) // ✅ name 变只刷这一行
Text(`age = ${this.user.age}`) // ✅ age 变只刷这一行
Button('age + 1').onClick(() => { this.user.age++ })
}
}
}
// @Track 与 @Observed 配合:@Track 必须在 @Observed 装饰的 class 中使用
@Track 与 @Observed 配合 vs 单独使用 @Track 的区别:@Track 与 @Observed 配合(@Track 必须在 @Observed 装饰的 class 中使用),单独使用 @Track(@Track 不在 @Observed 装饰的 class 中,不生效)。根因不是单独使用 @Track 是 @Track 与 @Observed 配合------@Track 与 @Observed 配合必须 @Observed 装饰 class,否则 @Track 不生效。
场景 3:@Track vs @Trace 边界(V1 属性级观测 vs V2 属性级观测)
@Track vs @Trace 边界------@Track 是 V1 属性级观测装饰器(API 11+),@Trace 是 V2 属性级观测装饰器(API 12+):
typescript
// ✅ 场景 3:@Track vs @Trace 边界(V1 属性级观测 vs V2 属性级观测)
// ✅ V1:@Track + @Observed(API 11+)
@Observed
class V1Model {
@Track name: string = 'V1' // ✅ @Track 装饰属性,V1 属性级观测
}
// ✅ V2:@Trace + @ObservedV2(API 12+)
@ObservedV2
class V2Model {
@Trace name: string = 'V2' // ✅ @Trace 装饰属性,V2 属性级观测
}
// ✅ 边界:
// @Track 是 V1 装饰器(API 11+),配合 @Observed,@Track 装饰属性变化只刷关联 UI
// @Trace 是 V2 装饰器(API 12+),配合 @ObservedV2,@Trace 装饰属性变化只刷关联 UI
// V1 用 @Track,V2 用 @Trace,不能混用(@Track 不在 @ObservedV2 中用,@Trace 不在 @Observed 中用)
@Track vs @Trace 边界的区别:@Track 是 V1 属性级观测装饰器(API 11+,配合 @Observed),@Trace 是 V2 属性级观测装饰器(API 12+,配合 @ObservedV2)。根因不是 V1 属性级观测是 @Track vs @Trace 边界------@Track 是 V1 装饰器配合 @Observed,@Trace 是 V2 装饰器配合 @ObservedV2,不能混用。
五、一句话哲学
写鸿蒙 ArkUI 记住 :@Track 不是整体刷新是 class 属性级精准观测------@Track 荬饰器 class 属性级观测只刷关联 UI,避免冗余刷新。根因不是整体刷新是属性级观测------@Track class 属性级观测(@Track 装饰属性变化只刷关联 UI)+ @Track 只刷关联 UI(str1 变只刷 str1 行,str2 行不刷,避免冗余刷新)+ @Track 限制条件(class 中用了 @Track,非 @Track 属性不能在 UI 中使用,运行时报错)+ @Track 与 @Observed 配合(必须 @Observed 装饰 class)+ @Track vs @Trace 边界(V1 用 @Track 配合 @Observed,V2 用 @Trace 配合 @ObservedV2,不能混用)。@Track 避免冗余刷新用属性级精准观测(首选,90% 场景),@Track 与 @Observed 配合必须 @Observed 装饰 class,@Track vs @Trace 边界选对装饰器(V1 用 @Track,V2 用 @Trace,不能混用)。class 属性级精准观测只刷关联 UI 避免冗余刷新是 ArkUI 状态联动深水区核心!