当前位置: 首页 > news >正文

ArkTS 进阶之道(7):@State 真做了啥?从赋值就刷 UI 理解依赖追踪

ArkTS 进阶之道(7):@State 真做了啥?从赋值就刷 UI 理解依赖追踪

本文是「ArkTS 进阶之道」系列第 7 篇,开「ArkUI 状态哲学」新阶段。前两阶段讲类型哲学(篇 50-52,推断逃逸点)+ 作用域哲学(篇 53-55,边界逃逸点)——都是编译期约束。本文换角度讲运行时:@State 装饰器真做了啥——它是 ArkUI 状态哲学的核心,赋值就刷 UI 不是魔法是依赖追踪机制。能力系列篇 13-19 讲过 @State 怎么用,本文讲为哈赋值就刷 UI——根因在依赖追踪机制。

一、开篇:@State 赋值就刷 UI 不是魔法,是依赖追踪机制

你写 TypeScript/React 时,状态赋值刷 UI 是「魔法」(React 的useStatesetter 调用才刷,直接赋值不刷):

// React useState setter 谰才刷 UI const [count, setCount] = useState(0) count++ ← 直接赋值,UI 不刷(React 不知道) setCount(count + 1) ← setter 调用,UI 獽刷(React 知道 setter 调了) // ArkTS @State 赋值就刷 UI(不是 setter 调用) @State count: number = 0 this.count++ ← 直接赋值就刷 UI,不用 setter

你写鸿蒙 ArkTS 时,@State 装饰器变量赋值就刷 UI——不是 setter 调用,是编译期生成的依赖追踪代码:

@Entry @Component struct Index { @State count: number = 0 ← @State 装饰器,赋值就刷 UI build() { Column() { Text(`${this.count}`) ← Text 依赖 this.count Button('点我') .onClick(() => { this.count++ ← 赋值就刷 UI,Text 自动更新 }) } } }

魔法 vs 机制的区别:React 把useStatesetter 当「魔法」(你调 setter 獽刷 UI,直接赋值不刷),ArkTS 把 @State 当「依赖追踪机制」(编译期生成追踪代码,赋值就刷 UI)。根因不是魔法是机制——@State 装饰器编译期给变量加依赖追踪,赋值触发追踪通知 UI 重渲。

二、根因:@State 的依赖追踪机制三重工作

鸿蒙 ArkUI 的 @State 装饰器是依赖追踪——编译期给变量加追踪代码,赋值触发通知 UI 重渲,来自三重机制工作。

机制 1:依赖追踪——编译期记 UI 哪里用了变量

@State 装饰器编译期给变量加依赖追踪——扫描 build() 里 UI 哪里用了这个变量,记成依赖关系:

@Entry @Component struct Index { @State count: number = 0 ← @State 装饰器,编译期加追踪 @State title: string = '标题' build() { Column() { Text(`${this.count}`) ← Text 依赖 this.count(编译期记依赖) Text(this.title) ← Text 依赖 this.title(编译期记依赖) Button('点我') .onClick(() => { this.count++ }) ← count 赋值触发通知 Text 重渲 } } }

编译期记依赖:@Statecount装饰器编译期扫描 build(),发现Text(${this.count})依赖count,记成依赖关系count → Text1。赋值this.count++触发通知Text1重渲。赋值就刷 UI 不是魔法,是编译期记的依赖关系触发通知。

机制 2:赋值触发通知——运行时赋值就发通知

@State 装饰器运行时赋值触发通知——赋值操作拦截成「发通知给依赖 UI」:

@Entry @Component struct Index { @State count: number = 0 plainCount: number = 0 ← 普通字段,赋值不发通知(对比) build() { Column() { Text(`@State = ${this.count}`) ← 依赖 @State count Text(`普通 = ${this.plainCount}`) ← 依赖普通 plainCount(但 plainCount 不发通知) Button('点我') .onClick(() => { this.count++ ← @State 赋值发通知,Text1 重渲 this.plainCount++ ← 普通赋值不发通知,Text2 不重渲(对比证据) }) } } }

赋值拦截通知:@Statecount�赋值this.count++被装饰器拦截成「赋值 + 发通知给 Text1」,Text1 重渲。普通plainCount�赋值不拦截不发通知,Text2 不重渲——这是 @State 装饰器赋值就刷 UI 的直接对比证据。

机制 3:UI 重渲——通知触发依赖 UI 重渲染

@State 装饰器通知触发UI 重渲——依赖这个变量的 UI 组件重渲染更新显示:

@Entry @Component struct Index { @State count: number = 0 @State list: string[] = ['一', '二', '三'] ← @State 数组也追踪 @State obj: INumObj = { a: 1, b: 2 } as INumObj ← @State 对象也追踪 build() { Column() { Text(`count = ${this.count}`) ← Text1 依赖 count Text(`list = ${this.list.join(', ')}`) ← Text2 依赖 list Text(`obj.a = ${this.obj.a}`) ← Text3 依赖 obj.a Button('基础类型') .onClick(() => { this.count++ }) ← count 赋值通知 Text1 重渲 Button('数组') .onClick(() => { this.list.push('新') }) ← list 负值通知 Text2 重渲 Button('对象属性') .onClick(() => { this.obj.a++ }) ← obj.a 赋值通知 Text3 重渲 } } }

通知触发重渲:@Statecount通知触发Text1重渲(显示新 count),@Statelist通知触发Text2重渲(显示新 list),@Stateobj通知触发Text3重渲(显示新 obj.a)。赋值就刷 UI 的完整链路:赋值 → 拦截通知 → 触发依赖 UI 重渲。

三、真机配图:@State 赋值就刷 UI vs 普通字段赋值不刷 UI 对比证据

初始态(stateCount=0、plainCount=0、plainCounter.count=0、stateList=一二三、stateObj.a=1 �均初始值):

点调四按钮后(stateCount=4 刷 UI、plainCount=1 不刷 UI、plainCounter.count=1 不刷 UI、stateList 含新5、stateObj.a=2 均真触发对比证据齐):

对比证据:@StatestateCount赋值就刷 UI(4 次点击显示 4),普通plainCount赋值不刷 UI(点击 4 次但显示仍 0),普通类plainCounter.count调 inc() 也不刷 UI(count=1 但 UI 不显示)。赋值就刷 UI 不是魔法是 @State 装饰器的依赖追踪机制——编译期记依赖 + 赋值拦截通知 + 触发重渲三重工作。

四、真解法:@State 用法的三个场景

场景 1:基础类型赋值刷 UI(number/string/boolean,90% 场景首选)

@Entry @Component struct Index { @State count: number = 0 ← @State 基础类型,赋值就刷 UI @State title: string = '标题' @State isOn: boolean = false build() { Column() { Text(`${this.count}`) ← 依赖 @State count Button('点我') .onClick(() => { this.count++ ← 赋值就刷 UI,不用 setter this.title = `标题${this.count}` ← 赋值就刷 UI this.isOn = !this.isOn ← 赋值就刷 UI }) } } }

为哈能跑:@State 基础类型(number/string/boolean)赋值触发依赖追踪通知,UI 重渲。首选这个,90% 的场景基础类型 @State 就够。赋值就刷 UI 不用 setter(跟 React useState 区别),ArkTS 编译期生成追踪代码。

场景 2:数组赋值刷 UI(push/splice �赋值刷 UI)

@Entry @Component struct Index { @State list: string[] = ['一', '二', '三'] ← @State 数组,赋值就刷 UI build() { Column() { Text(`${this.list.join(', ')}`) ← 依赖 @State list Button('加元素') .onClick(() => { this.list.push(`新${this.list.length + 1}`) ← push 负值就刷 UI }) Button('删元素') .onClick(() => { this.list.splice(0, 1) ← splice 负值就刷 UI }) } } }

为哈能跑:@State 数组push/splice/pop赋值触发依赖追踪通知,依赖数组的 UI 重渲。要写「列表动态增删」时用这个——赋值就刷 UI 不用额外通知,@State 数组追踪数组操作。

场景 3:对象属性赋值刷 UI(属性赋值刷 UI)

// 显式 interface 声明(见篇 51 装对象字量约束) interface INumObj { a: number b: number } @Entry @Component struct Index { @State obj: INumObj = { a: 1, b: 2 } as INumObj ← @State 对象,属性赋值就刷 UI build() { Column() { Text(`a = ${this.obj.a}, b = ${this.obj.b}`) ← 依赖 @State obj.a/obj.b Button('改 a') .onClick(() => { this.obj.a++ ← 属性赋值就刷 UI }) Button('改 b') .onClick(() => { this.obj.b = this.obj.b * 2 ← 属性赋值就刷 UI }) } } }

为哈能跑:@State 对象属性赋值(this.obj.a++)触发依赖追踪通知,依赖属性的 UI 重渲。要写「对象属性动态改」时用这个——属性赋值就刷 UI 不用整体替换,@State 对象追踪属性操作。注意装对象字量要as INumObj(见篇 51 装对象字量约束)。

五、一句话哲学

@State 赋值就刷 UI 不是魔法,是依赖追踪机制三重工作。ArkUI 的 @State 装饰器编译期给变量加依赖追踪——①编译期记 UI 哪里用了变量(依赖关系)②运行时赋值拦截成发通知(赋值触发通知)③通知触发依赖 UI 重渲(UI 重渲染)。三重工作链路:赋值 → 拦截通知 → 触发重渲。对比普通字段赋值不发通知不刷 UI,@State 装饰器赋值就刷 UI 是依赖追踪机制的直接证据。

两阶段 vs 状态哲学:类型哲学(篇 50-52,编译期推断逃逸点)+ 作用域哲学(篇 53-55,编译期边界逃逸点)都是编译期约束,状态哲学(篇 56,@State 运行时依赖追踪)是运行时机制——从「编译期拦」到「运行时追踪」换角度讲 ArkUI 状态哲学。

下一篇:ArkTS 进阶之道(8)—— @Prop/@Link 父子传值:单向 vs 双向数据流(对应能力系列篇 14,讲根因)——续「ArkUI 状态哲学」阶段。

能力系列回链

能力系列篇本文进阶点
篇 13 @State 基础用法@State 依赖追踪机制三重工作
篇 14 @Prop/@Link 用法下篇预告:单向 vs 双向数据流根因
篇 19 @State 数组/对象用法数组/对象属性赋值也追踪

真机 demo 完整代码

// 篇 56 demo:@State 装饰器依赖追踪机制 // 对比:@State 装饰器管赋值刷 UI vs 普通字段赋值不刷 UI // 普通字段(不是 @State):赋值不刷 UI class PlainCounter { count: number = 0 inc(): void { this.count++ } } // 显式 interface 声明(见篇 51 装对象字量约束) interface INumObj { a: number b: number } @Entry @Component struct Index { // ✅ @State 装饰器:赋值就刷 UI @State stateCount: number = 0 @State stateList: string[] = ['一', '二', '三'] @State stateObj: INumObj = { a: 1, b: 2 } as INumObj @State log: string = '(未操作)' // ❌ 普通字段:赋值不刷 UI(对比证据) plainCount: number = 0 plainCounter: PlainCounter = new PlainCounter() build() { Column({ space: 12 }) { Text('篇 56 配图:@State 装饰器依赖追踪机制') .fontSize(18).fontWeight(FontWeight.Bold).margin({ top: 20, bottom: 8 }) Text('@State 赋值就刷 UI vs 普通字段赋值不刷(对比证据)') .fontSize(12).fontColor('#888').margin({ bottom: 16 }) // @State 基础类型:赋值刷 UI Column({ space: 6 }) { Text(`@State stateCount = ${this.stateCount}`).fontSize(16).fontWeight(FontWeight.Bold) Text(`普通字段 plainCount = ${this.plainCount}(赋值不刷 UI)`).fontSize(14).fontColor('#999') Text(`普通类 plainCounter.count = ${this.plainCounter.count}(赋值不刷 UI)`).fontSize(14).fontColor('#999') Text(`@State stateList = ${this.stateList.join(', ')}`).fontSize(14) Text(`@State stateObj.a = ${this.stateObj.a}, b = ${this.stateObj.b}`).fontSize(14) Text(`日志:${this.log}`).fontSize(12).fontColor('#333').margin({ top: 4 }) } .width('92%').padding(12).backgroundColor('#f5f5f5').borderRadius(8) Button('@State 基础类型赋值(stateCount++)') .width('92%').height(44).fontSize(14) .onClick(() => { this.stateCount++ // ✅ @State 赋值就刷 UI this.plainCount++ // ❌ 普通字段赋值不刷 UI(对比) this.log = `第 ${this.stateCount} 次:@State 基础类型赋值刷 UI` }) Button('@State 数组赋值(push 新元素)') .width('92%').height(44).fontSize(14) .onClick(() => { this.stateList.push(`新${this.stateList.length + 1}`) // ✅ @State 数组赋值刷 UI this.log = `第 ${this.stateCount} 次:@State 数组 push 刷 UI,长度=${this.stateList.length}` }) Button('@State 对象属性赋值(stateObj.a++)') .width('92%').height(44).fontSize(14) .onClick(() => { this.stateObj.a++ // ✅ @State 对象属性赋值刷 UI this.log = `第 ${this.stateCount} 次:@State 对象属性 a++ 刷 UI,a=${this.stateObj.a}` }) Button('普通类调用(plainCounter.inc 不刷 UI)') .width('92%').height(44).fontSize(14) .onClick(() => { this.plainCounter.inc() // ❌ 普通类字段不刷 UI(对比) this.log = `第 ${this.stateCount} 次:plainCounter.inc() 调了但 UI 不刷,count=${this.plainCounter.count}` }) } .width('100%').height('100%').alignItems(HorizontalAlign.Center) } }

写鸿蒙 ArkUI 记住:@State 装饰器赋值就刷 UI 不是魔法是依赖追踪机制三重工作——①编译期记 UI 哪里用了变量(依赖关系)②运行时赋值拦截成发通知(赋值触发通知)③通知触发依赖 UI 重渲(UI 重渲染)。对比普通字段赋值不发通知不刷 UI,@State 装饰器赋值就刷 UI 是依赖追踪机制的直接证据。基础类型/数组/对象属性赋值均触发追踪,赋值就刷 UI 不用 setter是 ArkUI 状态哲学核心!

http://www.jsqmd.com/news/1284425/

相关文章:

  • 基于ESP32-C6与SHT40传感器的低功耗智能温湿度计设计与实现
  • Arduino按键处理优化:用C/C++宏实现高效事件驱动框架
  • Unity3D动态场景节点管理:架构设计与性能优化实战
  • AI音乐工业流水线可行性论证报告
  • (2026最新)蚌埠本地人必选的靠谱漏水检测维修推荐:正规防水补漏防水-卫生间/厨房/屋顶/阳台/外墙渗漏水精准测漏,本地人的信赖之选 - 安佳防水
  • GPS坐标转换:从十进制度到度分秒的原理、代码与实战
  • 172.2026年国家级科研瓶颈 | 机床热误差实时建模与补偿(温度场-变形场)
  • 抖大侠抖音小店自动拍单工具:密文下单物流回填与新手开店实操指南 - 抖大侠
  • 1/4波长天线设计全解析:从核心原理到Wi-Fi天线制作实战
  • AI设计工具效能衰减预警:你的Stable Diffusion提示词正在失效!3步重校准模型认知层(附权威测试报告)
  • AI浪潮来袭!小白也能抓住的收藏转行风口:月薪60k的AI大模型应用开发工程师
  • 基于Mathematica与Arduino的人脸跟踪云台系统设计与实现
  • HoRNDIS终极指南:如何在Mac上快速实现Android USB网络共享
  • 零基础上手桌面自动化 Agent,OpenClaw 2.7.9 分步安装排坑全指南
  • 【力扣hot100】双指针专题
  • AI 电动钟表眼镜超微型智能功率 覆盖微型电机驱动、电源管理、传感器控制的完整选型方案
  • 狗凯源码库学习资源真实价值评测
  • [特殊字符] “YOLO 模式” 首次曝光:AI 代理自主渗透泰国财政部,网络间谍进入全自动化时代
  • RK3568 Android 15驱动开发实战:从环境搭建到HAL集成
  • 2026年度江苏收售封边机公司优选指南:如何筛选靠谱伙伴 - 装修教育财税推荐2026
  • 基于ESP32-S3与百度智能云打造AI对话毛绒玩具:从硬件选型到系统整合
  • (2026最新)菏泽本地漏水检测维修公司靠谱推荐:正规防水补漏上门维修-墙面/屋顶/外墙/暗管漏水检测精准定位 - 即刻修防水
  • 物联网安全连接:A5000加密模块与PIC18F85J50的优化实践
  • 树莓派与Arduino协同开发:基于I2C通信的硬件集成与项目实战
  • 双向 DC/AC 全桥 PCS 电路工作过程与正负功率回路拆解
  • E语言函数参数详解:从基础到高级应用
  • 建筑建材行业变革在即,如何破解工程用材供货不稳、品质参差不齐行业痛点?木方/建材/建筑模板/木材/板材,木材供应商推荐 - 品牌推荐师
  • Linux内核6.x版本关键特性回顾:eBPF、io_uring与Rust支持的里程碑
  • AI 电动珠宝展示旋转台智能功率 MOSFET 完整选型方案
  • 计算机毕业设计之“友谊之家”旅行社平台系统的设计与实现