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

鸿蒙 HarmonyOS 6|ArkUI(03):状态管理

前言

我们这篇文章会把 ArkUI 在鸿蒙 6 里的状态管理讲清楚。

我们会按照三个层次来拆,也就是组件内局部状态、父子之间的同步、跨层的共享。我们会配上可运行的最小片段,并在最后给出一张决策表和一份常见误用清单。

一、先把三个层次的全景图说清楚

在 HarmonyOS 6 的 ArkUI 里,状态管理有一套清晰的装饰器体系。组件自己的可变数据用 @State 来承载,父子之间的单向同步用 @Prop 来承载,父子之间需要双向同步时用 @Link 来承载,跨多层的共享用 @Provide@Consume 来承载。

二、组件内局部状态:@State

@State 表示这个组件的本地可变数据。我们只要更新它,ArkUI 就会定位到依赖它的节点并触发重渲染。这个特性让我们可以放心把组件的细小交互放在本地管理,不必把所有变量都提到共享层。

@Component
export struct CounterCard {@State count: number = 0;build() {Column({ space: 10 }) {Text(`当前计数是 ${this.count}`).fontSize(16)Button('加一').onClick(() => {this.count += 1;})}.padding(12)}
}@Entry
@Component
struct Index {build() {Column({ space: 16 }) {CounterCard()}.justifyContent(FlexAlign.Center) // 让组件在竖直方向居中.width('100%').height('100%').padding(24)}
}

这段代码体现了状态驱动的基本套路。我们把界面写成状态的投影,用户点一次按钮,count 改一次,界面就会跟着更新。官方的状态章节明确说明了这种渲染绑定的机制和适用范围,我们保持就近封装就可以。

三、父子单向同步:@Prop

当父组件有一个数据需要交给子组件展示,但不希望子组件反向修改父组件时,我们使用 @Prop。父组件的状态变化会单向同步到子组件,而子组件对 @Prop 的修改不会再写回父组件的数据源。

@Component
export struct TitleView {@Prop title: stringbuild() {Text(this.title).fontSize(18).fontWeight(FontWeight.Medium)}
}@Component
export struct PageHeader {@State pageTitle: string = '我的页面'build() {Column({ space: 8 }) {TitleView({ title: this.pageTitle })Button('改标题').onClick(() => {this.pageTitle = '新的标题'})}.padding(12)}
}@Entry
@Component
struct Index {build() {Column({ space: 16 }) {PageHeader()}.justifyContent(FlexAlign.Center).width('100%').height('100%').padding(24)}
}

在这段代码里,PageHeader 持有真实状态,TitleView 只接收并展示。官方的 @Prop 文档还强调了初始化规则和与其他装饰器的组合关系,我们按文档理解数据流,就能避免意外的双向写回。

四、父子双向同步:@Link

当子组件需要既接到父组件的值,又要把自己的修改同步回父组件时,我们使用 @Link。这相当于在父子之间建立一条双向的通道,父变子跟,子改父也变,非常适合输入框这类场景。(Medium)

@Component
export struct NameInput {@Link name: stringbuild() {Column({ space: 8 }) {Text('请输入名字').fontSize(14)TextInput({ text: this.name }).onChange((v: string) => {this.name = v}).width('100%')}.padding(12)}
}@Component
export struct ProfileForm {@State userName: string = '小雨'build() {Column({ space: 10 }) {// 关键:@Link 需要按“引用”传参NameInput({ name: $userName })Text(`欢迎你,${this.userName}`).fontSize(16)}.padding(12)}
}@Entry
@Component
struct Index {build() {Column({ space: 16 }) {ProfileForm()}.justifyContent(FlexAlign.Center).width('100%').height('100%').padding(24)}
}

这里的 NameInput 收到初始值后,可以把输入变化回写到 ProfileForm

五、跨层共享:@Provide@Consume

当多个层级的组件都需要访问同一份数据,而中间层只是路过时,我们使用 @Provide@Consume。父辈通过 @Provide 暴露一个可共享的状态,后代通过 @Consume 直接拿到这份状态,并且形成双向同步的关系。

// 顶层提供共享状态
@Component
export struct AppShell {@Provide theme: 'light' | 'dark' = 'light'build() {Column({ space: 12 }) {ThemeSwitcher()ContentArea()}.padding(12)}
}// 任意后代直接消费共享状态
@Component
export struct ThemeSwitcher {@Consume theme: 'light' | 'dark'build() {Row({ space: 8 }) {Text(`当前主题是 ${this.theme}`)Button('切到浅色').onClick(() => this.theme = 'light')Button('切到深色').onClick(() => this.theme = 'dark')}}
}

这段代码说明了共享的真正含义。我们在顶层放一次 @Provide,就能让深层组件通过 @Consume 直接拿到同一份变量。官方文档明确指出两者之间是双向同步关系,所以后代的修改也会反馈到提供者的变量上,我们写共享逻辑时要有这个意识。

六、把状态和导航配合起来更顺手

在鸿蒙 6 的推荐导航范式里,我们通常会在入口页通过 @Provide 暴露一个 NavPathStack,让子页在需要返回时直接 pop,或者在需要进入下一个子页时 pushPath。这种写法把页面状态和页面栈都放在一个可控的范围里,跳转和返回更容易理解。

@Entry
@Component
export struct Index {@Provide('stack') stack: NavPathStack = new NavPathStack()@Builder pageMap(name: string) {if (name === 'Detail') {DetailPage()}}build() {Navigation(this.stack) {Button('进入详情').onClick(() => this.stack.pushPath({ name: 'Detail' }))}.navDestination(this.pageMap)}
}@Component
export struct DetailPage {@Consume('stack') stack: NavPathStackbuild() {NavDestination() {Button('返回').onClick(() => this.stack.pop())}}
}

我们把页面栈作为共享状态提供给后代,这样子页就不需要额外拿路由句柄了。官方的导航文档和相关问答都建议用这条思路来组织页面,代码更简洁,也更符合鸿蒙 6 的范式。(华为开发者官方网站)

七、选型决策表:先局部,后父子,再跨层

我们现在把日常开发里的常见情景放进一张表,方便快速决策。

场景推荐装饰器数据方向说明
组件内部的小交互@State组件内单点更新变更只影响本组件,渲染范围最小,成本最低。
父传子展示@Prop父到子单向子组件不回写,稳定可控,利于定位问题。
父子联动输入@Link父子双向仅在确需回写时使用,避免到处双向同步。
多层共享主题或会话@Provide/@Consume祖先与后代双向避免层层传参,控制好共享的边界与变更频率。

这张表背后的原则很直白。共享范围越小,优先级越高;能局部就局部,能单向就不双向,能父子就不要全局。等到确实需要跨层共享时,我们再引入 @Provide/@Consume,并把更新频率和依赖范围都控制住。

八、最佳实践清单

第一条建议是控制更新面。我们尽量在计算阶段使用临时变量,把最终结果一次性写回状态变量,这样可以减少多次写状态带来的重复渲染。

第二条建议是分层管理状态。我们把组件内状态与共享状态分开,避免把所有变量都提升为共享,这样渲染边界会更清楚。

第三条建议是谨慎使用双向同步。我们在能用单向数据流完成需求的情况下,就不要轻易引入 @Link,因为它会扩大可变范围。只有在输入控件确实需要回写到父组件时,才使用双向。

第四条建议是把跨层共享和导航解耦。我们让 @Provide/@Consume 只处理需要跨层的状态,让 Navigation 只处理页面栈,这样两个系统各司其职,问题更容易定位。

九、总结

我们已经把 HarmonyOS6 的 ArkUI 状态管理拆成了三个层次,并且用示例把每个层次都跑了起来。

http://www.dtcms.com/a/523961.html

相关文章:

  • DeepSeek 最新开源OCR模型,实测,不如百度Paddle
  • 做视频网站多大空间够网络推广是指什么
  • 网站运营维护中需要用到什么服务器网站设计哪家最好
  • 多类别分类中,标签的 “独热编码” 形式与输出层神经元的位置处理过程
  • 搞懂 Kotlin 的 List、Set、Map、HashMap、LinkedHashMap,以及 asSequence() 的底层原理与实战场景。
  • RK3568项目(十八)--debian文件系统的编译
  • 【Elasticsearch 全解析】分布式搜索引擎的原理、实践与优化
  • 亚马逊“Amelia”智能眼镜登场,三星/微美全息加速AI+AR技术融合引领穿戴赛道!
  • 成都有几个区高级seo培训
  • 免费网站模板 带后台网络网站维护费怎么做会计分录
  • Visual Studio 演进之路:从集成套件到AI驱动的开发平台
  • ament_make 详细范例
  • Git Stash 用法详解
  • tailwindcss使用@apply指令定义自己的样式
  • Ubuntu安装nvm(无需梯子自动连接github下载安装)
  • 襄阳云平台网站建设做网络竞拍的网站需要什么
  • 一个虚拟主机怎么做多个网站建立网站花钱吗
  • MySQL一篇速通
  • 用 Cloudflare + Gmail 免费搭建自定义域名邮箱(example.com 实操教程)
  • 02_prometheus监控Grafana展示
  • MYSQL之内置函数
  • 网站内容维护外包协议自己建网站难吗
  • Linux修炼:基础IO(二)
  • 什么是知识茧房,如何破除?是不是应该破除?
  • 李嘉诚发展史
  • Android15适配Edge
  • 标准NEMA语句GST及说明
  • php网站建设设计方法wordpress点击图片悬浮
  • Java的匿名内部类(重要)
  • 基于PCA算法降维设备多维度传感器数据