一、前言
状态管理是 ArkTS 开发的核心底层基石。页面文字、按钮禁用、列表数据、弹窗显隐、主题切换、用户信息缓存,所有会变化的UI,全部依赖状态驱动更新。
很多开发者写代码全靠“硬刷新页面”,不懂状态原理,导致项目后期极度难维护:
组件数据更新不刷新、刷新错乱、重复渲染;
父子组件传值单向、双向分不清,频繁出现数据不同步;
多层嵌套组件传值层层透传,代码冗余、维护爆炸;
全局状态滥用,页面互相污染、数据错乱、页面复现异常;
分不清哪种状态适合哪种场景,全部无脑用 @State,性能差、耦合重。
本篇一次性讲透鸿蒙六大状态体系,从基础用法、传值流向、底层刷新原理、场景选型、全局状态工程化封装全覆盖,读完彻底告别状态混乱。
二、鸿蒙所有状态装饰器总览
先搞懂全局选型,开发不再乱用注解。
装饰器 | 作用域 | 数据流 | 核心用途 |
|---|
@State | 组件内部 | 私有可变 | 组件自己的私有变化状态(按钮、显隐、输入框) |
@Prop | 子组件 | 父→子 单向 | 父传子,子组件只读,单向数据同步 |
@Link | 子组件 | 父⇋子 双向 | 父子双向绑定,子改父同步(弹窗、开关、输入框) |
@Provide | 祖辈组件 | 跨层下发 | 跨多层传值,无需逐层透传 |
@Consume | 后代组件 | 跨层接收 | 接收Provide数据,支持跨层双向更新 |
@Watch | 任意状态 | 监听回调 | 监听状态变化,执行回调业务逻辑 |
三、基础核心:@State 私有状态
@State 是最基础的状态,作用于当前组件内部,数据变更自动驱动当前组件UI刷新,属于组件私有数据。
3.1 基础用法
@Entry@Componentstruct StateDemo { // 组件私有状态 @State count: number = 0 build() { Column({ space: 20 }) { Text(`当前计数:${this.count}`) .fontSize(20) Button("点击+1") .onClick(() => { // 修改状态,自动刷新UI this.count++ }) } .width('100%') .height('100%') .justifyContent(FlexAlign.Center) }}
3.2 核心特点
仅当前组件可用,外部无法直接修改;
数据可变,赋值即刷新视图;
页面销毁自动回收,无全局污染。
适用场景:输入框内容、按钮禁用、弹窗显隐、计数器、开关状态等组件私有变量。
四、父子单向传值:@Prop
@Prop 用于 父组件向子组件单向传值,子组件数据跟随父组件变化,但子组件不能修改父数据,属于单向数据流。
4.1 父子单向传值实战
// 子组件@Componentstruct ChildPropItem { // 接收父组件传值,单向绑定 @Prop msg: string build() { Text(`子组件接收:${this.msg}`) .fontSize(18) .fontColor('#666') }}// 父组件@Entry@Componentstruct PropDemo { @State parentText: string = "默认父组件数据" build() { Column({ space: 30 }) { Button("修改父组件数据") .onClick(() => { this.parentText = "父组件数据已更新" }) ChildPropItem({ msg: this.parentText }) } .width('100%') .justifyContent(FlexAlign.Center) }}
父数据变 → 子自动变;子修改数据无效,保证单向数据流稳定。
五、父子双向绑定:@Link
@Link 是双向绑定,子组件修改数据,父组件同步更新,完美适配弹窗、开关、输入框等需要双向同步的场景。
5.1 双向绑定实战
// 子组件@Componentstruct LinkChildInput { // 双向绑定父组件状态 @Link inputValue: string build() { TextInput({ text: this.inputValue }) .width(300) .height(50) .onChange((val) => { // 子组件修改,父组件同步变化 this.inputValue = val }) }}// 父组件@Entry@Componentstruct LinkDemo { @State text: string = "" build() { Column({ space: 20 }) { Text(`父组件实时内容:${this.text}`) LinkChildInput({ inputValue: this.text }) } .justifyContent(FlexAlign.Center) .width('100%') }}
核心区别:Prop 单向只读,Link 双向互通。
六、跨多层组件传值:Provide / Consume
当组件嵌套层级很深(父→子→孙→曾孙),使用 Prop/Link 需要逐层传参,代码极其冗余。
@Provide + @Consume 实现跨层级穿透传值,祖辈直接给后代传值,无需层层透传。
6.1 跨层状态更新实战
// 最底层子组件@Componentstruct GrandChild { // 接收顶层Provide状态 @Consume themeColor: string build() { Text("深层子组件文本") .fontSize(20) .fontColor(this.themeColor) }}// 中层组件@Componentstruct Child { build() { GrandChild() }}// 顶层父组件@Entry@Componentstruct ProvideDemo { // 全局提供状态,所有后代可直接消费 @Provide themeColor: string = "#007DFF" build() { Column({ space: 20 }) { Button("切换颜色") .onClick(() => { this.themeColor = "#22c55e" }) Child() } }}
顶层状态修改,深层组件自动刷新,彻底解决props逐层透传地狱。
七、状态监听:@Watch 监听状态变化
业务中经常需要:数据变化后自动请求接口、刷新列表、弹窗提示。@Watch 可以监听任意状态变更,触发回调函数。
@Entry@Componentstruct WatchDemo { @State @Watch("onCountChange") count: number = 0 // 状态变化回调 onCountChange(newVal: number, oldVal: number) { console.log(`旧值:${oldVal},新值:${newVal}`) // 可执行接口请求、业务逻辑 } build() { Column() { Text(`计数:${this.count}`) Button("增加") .onClick(() => this.count++) } }}
八、全局状态管理(商用项目必备)
页面之间共享数据(用户信息、登录状态、主题、全局配置),不能用页面级状态,需要全局单例状态。
8.1 全局状态仓库封装
// store/GlobalStore.etsexport default class GlobalStore { // 单例 private static instance: GlobalStore public static getInstance(): GlobalStore { if (!this.instance) { this.instance = new GlobalStore() } return this.instance } // 全局用户状态 userName: string = "未登录" isLogin: boolean = false // 修改全局状态方法 setUserInfo(name: string, login: boolean) { this.userName = name this.isLogin = login }}
8.2 页面使用全局状态
import GlobalStore from '../store/GlobalStore'@Entry@Componentstruct GlobalStatePage { // 全局实例 store = GlobalStore.getInstance() build() { Column({ space: 20 }) { Text(`登录状态:${this.store.isLogin ? "已登录" : "未登录"}`) Text(`用户名:${this.store.userName}`) Button("模拟登录") .onClick(() => { this.store.setUserInfo("鸿蒙开发者", true) }) } .width('100%') .justifyContent(FlexAlign.Center) }}
九、状态开发高频坑点
- @Prop 试图修改父数据:单向数据流,子修改不会同步父,导致数据不同步Bug;
- 多层嵌套滥用Link:层级过深代码混乱,优先使用 Provide/Consume;
- 全局状态随意修改:无封装方法,页面直接篡改属性,难以溯源;
- 复杂对象直接赋值:状态无法监听,UI不刷新,需要解构或整体替换;
- 忘记Watch新旧参数:无法比对数据变化,重复执行业务逻辑。
十、全文总结
鸿蒙状态管理有明确的层级分工:组件内部变化用@State、父子单向传值用 @Prop、双向同步用 @Link、跨层传值用 Provide/Consume、状态监听用 @Watch、页面共享用全局Store。
商用项目想要代码整洁、数据稳定、渲染高性能,必须严格遵守数据流规范,杜绝乱用状态、暴力刷新页面,这是进阶鸿蒙开发工程师的必备能力。