《ArkTS 状态管理选型:@State、@Prop、@Link 与 @Provide/@Consume 怎么选》
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 管跨层。归属清楚了,代码里的状态流向自然清楚,出了问题也知道该去哪一层查。
- 点赞
- 收藏
- 关注作者
评论(0)