Harmony 状态管理 @Local 和 @Param
@Local 背景
@Local 是harmony应用开发中的v2版本中 对标**@State**的状态管理修饰器,它解决了 @State 对状态变量更改的检测混乱的问题:
- @State 修饰的状态变量 可以是组件内部自己定义的
- @State 修饰的状态 也可以由外部父组件传递
这样就导致了状态数据来源不唯一,在大型项目中会引发难易检测和维护状态的问题。如以下代码:
@Entry @Component struct Index { @State num: number = 100 build() { Column() { Text("父组件的数据 " + this.num) Son({ num: this.num }) Son() } .height('100%') .width('100%') } } @Component struct Son { @State num: number = 0 build() { Column() { Button(`子组件 ${this.num}`) .onClick(() => { this.num++ }) } } }
@Local 基本使用
@Local的出现就是为了解决这一类问题
- @Local 只能用在 @Componentv2 修饰的组件上
- 被**@Local**装饰的变量无法从外部初始化(无法由父组件传递进来),因此必须在组件内部进行初始化
我们对上面代码稍作修改
@Entry @Component struct Index { @State num: number = 100 build() { Column() { Text("父组件的数据 " + this.num) Son({ num: this.num }) // 这里就报错啦 Son() } .height('100%') .width('100%') } } @ComponentV2 // 此处调整为 @ComponentV2 struct Son { // 此处调整为 @Local @Local num: number = 0 build() { Column() { Button(`子组件 ${this.num}`) .onClick(() => { this.num++ }) } } }
@Local与@State对比
@State | @Local | |
参数 | 无。 | 无。 |
从父组件初始化 | 可选。 | 不允许外部初始化。 |
观察能力 | 能观测变量本身以及一层的成员属性,无法深度观测。 | 能观测变量本身,深度观测依赖@Trace装饰器。 |
数据传递 | 可以作为数据源和子组件中状态变量同步。 | 可以作为数据源和子组件中状态变量同步。 |
@Local 特别注意
- @Local支持观测number、boolean、string、Object、class等基本类型以及Array、Set、Map、Date等内嵌类型。
- @Local的观测能力仅限于被装饰的变量本身。当装饰简单类型时,能够观测到对变量的赋值;当装饰对象类型时,仅能观测到对对象整体的赋值;当装饰数组类型时,能观测到数组整体以及数组元素项的变化;当装饰Array、Set、Map、Date等内嵌类型时,可以观测到通过API调用带来的变化。
@Params
@Params主要表示 子组件 接收父组件传递的数据。可以和 @Local 搭配一起使用
@Params 背景
在V1版本的状态管理修饰符中,可以用来处理 父子传参的技术有:
- 普通属性,不需要特别的修饰符 , 不具备单向同步
- @Prop 单向同步,不能监听深层次属性的改变,也不能做到双向同步
- @Link 可以做到双向同步,但是不能监听深层次属性的改变,而且不能直接用在 列表渲染技术 - ForEach 中
- @ObjectLink 可以做到双向同步,但是必须和 @Observed 搭配使用 ,而且只能用在自定义组件上
1. 普通属性
普通属性,不需要特别的修饰符 , 不具备单向同步
@Entry @Component struct Index { @State num: number = 100 build() { Column() { // 父组件传递过去的数据 Son({ num: this.num }) .onClick(() => { this.num++ console.log("", this.num) }) } .height('100%') .width('100%') } } @Component struct Son { num: number = 0 build() { Column() { Button(`子组件 ${this.num}`) } } }
2. @Prop 单向同步
@Prop 单向同步
- 不能监听深层次属性的改变
- 也不能做到双向同步
在上面代码基础上 加入**@Prop**,可以检测到基本类型数据的更新
@Component struct Son { @Prop num: number = 0
但是无法检测深层次属性的改变,如
class Animal { dog: Dog = { age: 100 } } class Dog { age: number = 100 } @Entry @Component struct Index { @State animal: Animal = new Animal() build() { Column() { // 父组件传递过去的数据 Son({ dog: this.animal.dog }) .onClick(() => { this.animal.dog.age++ console.log("", this.animal.dog.age) }) } .height('100%') .width('100%') } } @Component struct Son { @Prop dog: Dog build() { Column() { Button(`子组件 ${this.dog.age}`) } } }
3. @Link 双向同步
@Link 用法和@Prop基本一致
可以做到双向同步,但是
- 不能监听深层次属性的改变
- 而且不能直接用在 列表渲染技术 - ForEach 中 以下代码演示这个效果
class Dog { age: number = 100 } @Entry @Component struct Index { @State dogList: Dog [] = [new Dog(), new Dog(), new Dog(), new Dog()] build() { Column() { ForEach(this.dogList, (item: Dog) => { // 此处会报错 Assigning the attribute 'item' to the '@Link' decorated attribute 'dog' is not allowed. <ArkTSCheck> Son({ dog: item }) .onClick(() => { item.age++ console.log("", item.age) }) }) } .height('100%') .width('100%') } } @Component struct Son { @Link dog: Dog build() { Column() { Button(`子组件 ${this.dog.age}`) } } }
4. @ObjectLink
@ObjectLink 可以做到双向同步,但是必须和 @Observed 搭配使用 ,而且只能用在自定义组件上
小结
可以看到,如果都是使用 v1版本的这一套 父子传参的技术,是十分复杂难易直接上手使用的。
@Params 介绍
Param表示组件从外部传入的状态,使得父子组件之间的数据能够进行同步:
- @Param装饰的变量支持本地初始化,但是不允许在组件内部直接修改变量本身。
- 如果不本地初始化,那么必须加入 @Require
- @Param 可以做到单向同步
- @Param 可以检测深层次属性的修改,但是该修改在数据源上必须是整体对象的更新
- @Params 如果也想要深度监听单个属性的修改,那么需要使用 @ObservedV2 和 @Trace
以下代码主要演示:@Param 可以检测深层次属性的修改,但是该修改在数据源上必须是整体对象的更新
class Person { age: number = 100 } @Entry @ComponentV2 struct Index { @Local person: Person = new Person() build() { Column() { Son({ age: this.person.age }) .onClick(() => { this.person.age++ console.log("", this.person.age) if (this.person.age === 105) { const p = new Person() p.age = 200 // 整体更新,子组件可以感知到 this.person = p } }) } .height('100%') .width('100%') } } @ComponentV2 struct Son { // 要么设置 @Require 表示父组件必须传递数据 // 要么设置 默认值 @Require @Param age: number build() { Column() { Button(`子组件 ${this.age}`) } } }
总结
- @Local 可以看成是 @State的替代 ,单独表示组件内部的状态
- @Params 可以看成 @Prop @Link @ObjectLink的替代,更加严谨
- @Local 和 @Params 搭配一起使用,都只能用在 @Componentv2 修饰的自定义组件上