#HarmonyOS篇:管理组件拥有的状态
官网链接 @Prop @Link等
@State
@State声明的变量,必须给初始值,否则编辑报错
不支持装饰Function类型。
@Prop
基本用法
@Prop装饰的变量允许本地修改,但修改不会同步回父组件。
子组件@Prop更新时,更新仅停留在当前子组件,不会同步回父组件。
不允许装饰Function类型
父组件传递的值 子组件不用@Prop接收也可以渲染,只不过子组件改值不会自动更新视图
使用a.b(this.object)形式调用,不会触发UI刷新
通过赋值添加 Proxy 代理 let score1 = this.score
class Score {value: number;constructor(value: number) {this.value = value;}static changeScore1(score:Score) {score.value += 1;}
}@Entry
@Component
struct Parent {@State score: Score = new Score(1);build() {Column({space:8}) {Text(`The value in Parent is ${this.score.value}.`).fontSize(30).fontColor(Color.Red)Child({ score: this.score })}.width('100%').height('100%')}
}@Component
struct Child {@Prop score: Score;changeScore2(score:Score) {score.value += 2;}build() {Column({space:8}) {Text(`The value in Child is ${this.score.value}.`).fontSize(30)Button(`changeScore1`).onClick(()=>{// 通过赋值添加 Proxy 代理let score1 = this.score;Score.changeScore1(score1);})Button(`changeScore2`).onClick(()=>{// 通过赋值添加 Proxy 代理let score2 = this.score;this.changeScore2(score2);})}}
}
@Link
注意事项
- List item
@Link装饰器不建议在@x`Entry装饰的自定义组件中使用,否则编译时会抛出警告;若该自定义组件作为页面根节点使用,则会抛出运行时错误。
-
@Link装饰的变量禁止本地初始化,否则编译期会报错。
-
@Link装饰的变量的类型要和数据源类型保持一致,否则编译期会报错。同时,数据源必须是状态变量,否则框架会抛出运行时错误。
-
@Link装饰的变量仅能被状态变量初始化,不能使用常规变量初始化,否则编译期会给出告警,并在运行时崩溃
@Link name: string | undefined; ----支持联合类型
@Watch监听的写法
@Link @Watch('onChangeSonToP') SonToParentText: string;onChangeSonToP() {console.info('111')}
@Provide @Consume 双向同步
// 提供@Provide parentArg: string = ' 父组件提供--provide'// 获取@Consume parentArg: string;
@Observed装饰器和@ObjectLink装饰器:嵌套类对象属性变化
注意事项
@ObjectLink装饰的变量不能被赋值,如果要使用赋值操作,请使用@Prop。
@ObjectLink装饰的变量和数据源的关系是双向同步
@Observed装饰的类,如果其属性为非简单类型,比如class、Object或者数组,那么这些属性也需要被@Observed装饰,否则将观察不到这些属性的变化。
定义
从父组件初始化
必须指定。
初始化@ObjectLink装饰的变量必须同时满足以下场景:
- 类型必须是@Observed装饰的class。
- 初始化的数值需要是数组项,或者class的属性。
示例代码
@Observed
class Book {name: string;constructor(name: string) {this.name = name;}
}@Observed
class Bag {book: Book;constructor(book: Book) {this.book = book;}
}@Component
struct BookCard {@ObjectLink book: Book;build() {Column() {Text(`BookCard: ${this.book.name}`) // 可以观察到name的变化.width(320).margin(10).textAlign(TextAlign.Center)Button('change book.name').width(320).margin(10).onClick(() => {this.book.name = 'C++';})}}
}@Entry
@Component
struct Index {@State bag: Bag = new Bag(new Book('JS'));build() {Column() {Text(`Index: ${this.bag.book.name}`) // 无法观察到name的变化.width(320).margin(10).textAlign(TextAlign.Center)Button('change bag.book.name').width(320).margin(10).onClick(() => {this.bag.book.name = 'TS';})BookCard({ book: this.bag.book })}}
}
@Track
@Track是class对象的属性装饰器。当一个class对象是状态变量时,@Track装饰的属性发生变化,只会触发该属性关联的UI更新;如果class类中使用了@Track装饰器,则未被@Track装饰器装饰的属性不能在UI中使用,如果使用,会发生运行时报错。
class TrackLock {@Track name: string;@Track age: string;constructor(name: string, age: string) {this.name = namethis.age = age}
}class TrackNoLock {name1: string;age1: stringconstructor(name1: string, age1: string) {this.name1 = name1this.age1 = age1}
}@Component
export struct TrackTest {@State trackLock: TrackLock = new TrackLock('小明', '15')@State trackNoLock: TrackNoLock = new TrackNoLock('小明1', '18')isRender(index: number) {console.info(`Text ${index} is rendered`);return 50;}build() {Column() {Text('trackLock-测试')Text(this.trackLock.name).fontSize(this.isRender(1))Text(this.trackLock.age).fontSize(this.isRender(2))Text('trackNoLock-测试')Text(this.trackNoLock.name1).fontSize(this.isRender(3))Text(this.trackNoLock.age1).fontSize(this.isRender(4))Button('测试').onClick((event: ClickEvent) => {this.trackLock.name = '11'// 输入 Text 1 is rendered})Button('change logNotTrack.str1').onClick(() => {this.trackNoLock.name1 = '再见';// 输出Text 3 is renderedText 4 is rendered})}}
}
@Builder装饰器:自定义构建函数
arkUI提供轻量的UI元素复用机制@Builder,其内部UI结构固定,仅与使用方进行数据传递。开发者可将重复使用的UI元素抽象成函数,在build函数中调用。
@Builder装饰的函数也称为“自定义构建函数”。
封装可复用的UI结构
@LocalBuilder维持组件关系
注意事项
-
@LocalBuilder只能在所属组件内声明,不允许全局声明。
-
@LocalBuilder不能与内置装饰器或自定义装饰器一起使用。
-
在自定义组件中,@LocalBuilder不能用来装饰静态函数。
== 允许在自定义组件内定义一个或多个@LocalBuilder函数,该函数被认为是该组件的私有、特殊类型的成员函数。==
@LocalBuilder和局部@Builder使用区别
跨组件传递局部@Builder函数时,会使用.bind(this)更改函数上下文,但这可能会导致组件的父子关系与状态管理的父子关系不一致。而@LocalBuilder无论是否使用.bind(this),都不会改变组件的父子关系,即@LocalBuilder中定义组件所属的父组件是确定的,无法被改变。
//----------------------------- 传递 父组件 Parentlabel: string = 'Parent';@LocalBuilderbuilderChild() {Text('Builder-child---' + this.label)}
// ------------------
//---------------------------- 使用 在父组件中使用Row() {Text('父')this.builderChild()}Child({ customBuilderParams: this.builderChild })
//-------------------------- 子组件接收 Childlabel: string = 'Child';@BuilderParam customBuilderParams: () => void
@BuilderParam装饰器:引用@Builder函数
@BuilderParam用于装饰指向@Builder方法的变量,通过调用@BuilderParam为组件增加特定功能。
如果不这样的话会导致所有自定义组件都增加此实例方法。
@BuilderParam装饰的方法只能被自定义构建函数(@Builder装饰的方法)初始化。
@Styles支持全局和局部 不支持传参数
@Extend仅支持全局定义 支持传递参数
@Require校验构造参数
当Child组件内使用@Require装饰器和@Prop、@State、@Provide、@BuilderParam、@Param和普通变量(无状态装饰器修饰的变量)结合使用时,父组件Index在构造Child时必须传参,否则编译不通过。
