《ArkTS 状态管理选型:@State、@Prop、@Link 与 @Provide/@Consume 怎么选》

举报
yd_295436472 发表于 2026/11/07 10:41:29 2026/11/07
【摘要】 ArkTS 状态管理选型:@State、@Prop、@Link 与 @Provide/@Consume 怎么选组件一多,状态就容易乱:父组件改了子组件没反应,兄弟组件各管各的,页面一刷新数据全丢。本文把 ArkTS 里最常用的四种状态装饰器放在一起,对照它们的归属、同步方向和适用场景,给出一套选型思路。 一、先回答一个问题:状态属于谁状态管理混乱,根源往往是“不知道这个状态归谁管”。先定归...

ArkTS 状态管理选型:@State、@Prop、@Link 与 @Provide/@Consume 怎么选

组件一多,状态就容易乱:父组件改了子组件没反应,兄弟组件各管各的,页面一刷新数据全丢。本文把 ArkTS 里最常用的四种状态装饰器放在一起,对照它们的归属、同步方向和适用场景,给出一套选型思路。

一、先回答一个问题:状态属于谁

状态管理混乱,根源往往是“不知道这个状态归谁管”。先定归属,再选装饰器:

  • 只在当前组件内部用:归组件自己管。
  • 父组件拥有、子组件只读:归父组件管,子组件拿一份单向同步的副本。
  • 父子需要双向同步:归父组件管,子组件可以回写。
  • 跨层级、多组件共享:归共同的祖先管,或抽成共享数据源。

归属定下来,装饰器的选择就只剩一半工作。

二、@State:组件自己的状态

@State 修饰的状态是组件私有的,变化会触发当前组件重新渲染。

@Component
struct Counter {
  @State count: number = 0;
  build() {
    Column() {
      Text(`点击次数:${this.count}`)
      Button('点我').onClick(() => { this.count++ })
    }
  }
}

适用场景:开关状态、输入框内容、临时展开/收起。注意两点:其一,@State 只能在组件内部初始化,不要把它当成跨组件通信的通道;其二,状态变化会触发整个 build 重新执行,频繁变化的状态要尽量拆细,避免一次改动重绘整个页面。

三、@Prop:父传子,单向同步

@Prop 接收父组件传下来的值,父组件变化时子组件同步更新,但子组件不能反向修改它——本地改动会被父组件下一次更新覆盖。

@Component
struct Child {
  @Prop title: string = '';
  build() {
    Text(this.title).fontSize(16)
  }
}

@Component
struct Parent {
  @State title: string = '你好';
  build() {
    Child({ title: this.title })
  }
}

适用场景:列表项展示、配置下发、只读参数传递。如果子组件确实需要改这个值,说明它不该是 @Prop,往下看 @Link。

四、@Link:父子双向同步

@Link 与父组件的 @State 建立双向绑定,子组件修改会同步回父组件,父组件修改也会同步到子组件。

@Component
struct Editor {
  @Link text: string = '';
  build() {
    TextInput({ text: this.text })
      .onChange((value: string) => { this.text = value })
  }
}

@Component
struct Parent {
  @State text: string = '初始文本';
  build() {
    Editor({ text: this.$text })
  }
}

注意父组件传参时要写 this.$text,这是建立双向绑定的语法。适用场景:表单输入、可编辑的列表项、开关控件。滥用 @Link 会让数据流向难以追踪:一个状态被三四个子组件同时回写时,出问题很难定位谁改的。原则是:能用 @Prop 解决的,不用 @Link。

五、@Provide / @Consume:跨层级共享

逐层传参写起来很啰嗦:祖先的状态要经过中间两三层“二传手”才能到目标组件。@Provide 在祖先组件提供状态,@Consume 在任意后代组件消费,中间组件不需要参与。

@Component
struct Root {
  @Provide theme: string = 'light';
  build() {
    Column() {
      Middle()
    }
  }
}

@Component
struct DeepChild {
  @Consume theme: string = '';
  build() {
    Text(`当前主题:${this.theme}`)
  }
}

适用场景:主题、用户信息、全局配置等“很多页面都要读”的状态。注意:@Provide / @Consume 也是双向的,消费方修改会同步回提供方;名字必须一致,拼写错一个字母就会静默失效,调试时先检查名称。

六、一张选型表

场景 选谁
组件内部临时状态 @State
父组件拥有、子组件只读 @Prop
父子需要双向同步 @Link
跨多层共享的状态 @Provide / @Consume
多页面共享、生命周期长的全局状态 抽到 AppStorage 或状态管理库

七、容易踩的三个坑

  • 用 @Prop 回写数据:子组件改了 @Prop,以为同步回去了,父组件一刷新又被覆盖。需要回写就换 @Link,或者用事件回调通知父组件改。
  • @State 里放大对象:状态对象越大,一次改动触发的 diff 成本越高。把“经常变的字段”和“很少变的字段”拆开存放。
  • 跨页面共享还用 @Provide:@Provide 只在组件树内有效,跨页面要走 AppStorage、持久化存储或专门的状态管理方案,别硬套。

结语

状态管理没有银弹,关键是先定归属:谁拥有、谁只读、谁能改。@State 管自己,@Prop 管单向,@Link 管双向,@Provide/@Consume 管跨层。归属清楚了,代码里的状态流向自然清楚,出了问题也知道该去哪一层查。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。