欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 汽车 > 新车 > HarmonyOS第六章:组件状态共享(父子组件传参、多层级组件传参、@Watch监听状态变化、@Observed与@ObjectLink、多层嵌套数据更新)

HarmonyOS第六章:组件状态共享(父子组件传参、多层级组件传参、@Watch监听状态变化、@Observed与@ObjectLink、多层嵌套数据更新)

2024/10/24 18:27:51 来源:https://blog.csdn.net/to_the_Future/article/details/141066173  浏览:    关键词:HarmonyOS第六章:组件状态共享(父子组件传参、多层级组件传参、@Watch监听状态变化、@Observed与@ObjectLink、多层嵌套数据更新)

🎉 博客主页:【剑九_六千里-CSDN博客】【剑九_六千里-掘金社区】
🎨 上一篇文章:【HarmonyOS第五章:组件抽取、构建函数抽取@Builder、构建函数插槽@BuilderParam】
🎠 系列专栏:【HarmonyOS系列】
💖 感谢大家点赞👍收藏⭐评论✍

在这里插入图片描述

在这里插入图片描述

引言:
ArkUI 框架,通过一系列装饰器(Decorators)提供了强大的数据绑定机制,使得开发者能够轻松地管理应用的状态并实现高效的视图更新。本文将深入探讨 ArkUI 中几种关键装饰器的功能及使用场景,包括 @Observed@Watch $$ 语法等。

文章目录

  • 1. 父子组件传值-单向数据流
    • 1.1. 直接传递
    • 1.2. @Prop变量装饰器
  • 2. 父子组件传值-双向数据流
    • 2.1. @Link
      • 2.1.1. 使用示例-基本的数据传递:
      • 2.1.2. 使用示例-父组件修改数据:
      • 2.1.3. 使用示例-子组件修改数据源:
      • 2.1.4. 动态创建Link
  • 3. 多层级组件之间传值
    • 3.1. @Provide和@Consume
      • 3.1.1. 使用示例-给孙组件传递数据:
      • 3.1.2. 使用示例-父组件修改数据:
      • 3.1.3. 使用示例-后代组件修改数据:
  • 4. @Observed 与 @ObjectLink 嵌套类对象属性变化
    • 4.1. 下面这么传递的数据,在子组件中不能修改:
    • 4.2. 使用@Observed 和 @ObjectLink:
    • 4.3. 可以发现,当我们在子组件中修改数据时,子组件试图更新了,那么父组件是否更新呢?再看下面这个例子:
    • 4.4. 解决 @Observed 与 @ObjectLink 试图更新问题:
  • 5. @Watch 装饰器:状态变量更改通知
    • 5.1. 使用示例-@Watch和@Prop结合使用
    • 5.2. 使用示例-@Watch和@Link结合使用
    • 5.3. 使用示例-@Watch和@Provide结合使用
  • 7. $$语法:内置组件双向同步
    • 7.1. 使用规则
    • 7.2. 使用示例

1. 父子组件传值-单向数据流

1.1. 直接传递

  • 父组件使用子组件时,可直接按引用传递值

在这里插入图片描述
使用示例:

@Entry
@Component
struct Parent {@State count: number = 5;build() {Column() {Text(`这是父组件------${this.count}`)Child({count: this.count})}.height("100%").width("100%")}
}@Component
struct Child {count: number = 0;build() {Text(`这是子组件------${this.count}`)}
}
  • 父组件修改数据源时,传递给子组件的数据不更新

此时通过点击按钮,已经修改了数据源的数据,但子组件中并没有更新:

在这里插入图片描述

使用示例:

@Entry
@Component
struct Parent {@State count: number = 5;build() {Column() {Text(`这是父组件------${this.count}`)Button("修改count").onClick(() => {this.count++;})Child({count: this.count})}.height("100%").width("100%")}
}@Component
struct Child {count: number = 0;build() {Text(`这是子组件------${this.count}`)}
}
  • 子组件不能修改数据

使用示例:
子组件修改数据,无反应,因为数据非响应式:

@Entry
@Component
struct Parent {@State count: number = 5;build() {Column() {Text(`这是父组件------${this.count}`)Button("修改count").onClick(() => {this.count++;})Child({count: this.count})}.height("100%").width("100%")}
}@Component
struct Child {count: number = 0;build() {Column() {Text(`这是子组件------${this.count}`)Button("修改count").onClick(() => {this.count++;})}}
}

1.2. @Prop变量装饰器

参数同步类型允许装饰的变量类型
单向同步string、number、boolean、enum类型,不支持any,不允许使用undefined和null,必须指定类型
  • 使用 @Prop 接收的参数可在子组件直接使用
  • 使用 @Prop 接收的参数,父组件修改数据,子组件会同步更新

在这里插入图片描述
使用示例:

@Entry
@Component
struct Parent {@State count: number = 5;build() {Column() {Text(`这是父组件------${this.count}`)Button("修改count").onClick(() => {this.count++;})Child({count: this.count})}.height("100%").width("100%")}
}@Component
struct Child {@Prop count: number = 0;build() {Column() {Text(`这是子组件------${this.count}`)}}
}
  • @Prop装饰的变量可以在本地修改,但修改后的变化不会同步回其父组件,采用单向数据流的模式
    在这里插入图片描述

使用示例:

@Entry
@Component
struct Parent {@State count: number = 5;build() {Column() {Text(`这是父组件------${this.count}`)Button("修改count").onClick(() => {this.count++;})Child({count: this.count})}.height("100%").width("100%")}
}@Component
struct Child {@Prop count: number = 0;build() {Column() {Text(`这是子组件------${this.count}`)Button("修改count").onClick(() => {this.count++;})}}
}

2. 父子组件传值-双向数据流

2.1. @Link

参数同步类型允许装饰的变量类型
双向同步Object、class、string、number、boolean、enum类型,以及这些类型的数组,不支持any,不允许使用undefined和null,不支持Length、ResourceStr、ResourceColor类型
  • @Link 装饰的变量与其父组件中对应的数据源建立双向数据绑定,实现了双向同步。
  • 不能在 @Entry 装饰的自定义组件中使用。
  • @Link 修饰时数据不能设置默认值。
  • 父组件传递参数时,参数不能用 this.XXX,必须使用 $XXX
  • @Link 传递复杂数据类型时,只能将数据整体传递,不能只传递其中某一项。

2.1.1. 使用示例-基本的数据传递:

在这里插入图片描述

@Entry
@Component
struct Parent {@State count: number = 5;build() {Column() {Text(`这是父组件------${this.count}`)Child({count: $count}) // 必须使用$}.height("100%").width("100%")}
}@Component
struct Child {@Link count: number; // 不能设置默认值build() {Column() {Text(`这是子组件------${this.count}`)}}
}

2.1.2. 使用示例-父组件修改数据:

在这里插入图片描述

@Entry
@Component
struct Parent {@State count: number = 5;build() {Column() {Text(`这是父组件------${this.count}`)Button("修改count").onClick(() => {this.count++;})Child({count: $count}) // 必须使用$}.height("100%").width("100%")}
}@Component
struct Child {@Link count: number; // 不能设置默认值build() {Column() {Text(`这是子组件------${this.count}`)}}
}

2.1.3. 使用示例-子组件修改数据源:

@Entry
@Component
struct Parent {@State count: number = 5;build() {Column() {Text(`这是父组件------${this.count}`)Button("修改count").onClick(() => {this.count++;})Child({count: $count}) // 必须使用$}.height("100%").width("100%")}
}@Component
struct Child {@Link count: number; // 不能设置默认值build() {Column() {Text(`这是子组件------${this.count}`)Button("修改count").onClick(() => {this.count++;})}}
}

2.1.4. 动态创建Link

3. 多层级组件之间传值

3.1. @Provide和@Consume

  • 父组件通过 @Provide 声明变量,@Provide 装饰的状态变量自动对其所有后代组件可用,即该变量被“provide”给他的后代组件。不需要多次在组件之间传递变量。
  • 后代通过使用 @Consume 去获取 @Provide 提供的变量,建立在 @Provide@Consume 之间的双向数据同步,与@State/@Link不同的是,前者可以在多层级的父子组件之间传递。
  • 父组件修改数据,后代组件同步更新。
  • 后代组件修改数据,父组件同步更新。
  • 接收数据时,不能设置默认值。
  • 允许传递 Objectclassstringnumberbooleanenum类型,以及这些类型的数组。

3.1.1. 使用示例-给孙组件传递数据:

在这里插入图片描述

@Entry
@Component
struct Parent {@Provide count: number = 5;build() {Column() {Text(`这是父组件------${this.count}`).width("100%").height(100).backgroundColor(Color.Red)Child()}.height("100%").width("100%")}
}@Component
struct Child {build() {Column() {Text(`这是子组件`)GrandSon()}.width("100%").height(200).backgroundColor(Color.Green)}
}@Component
struct GrandSon {@Consume count: number; // 不能设置默认值build() {Column() {Text(`这是孙组件------${this.count}`).width("100%").height(100).backgroundColor(Color.Yellow)}}
}

3.1.2. 使用示例-父组件修改数据:

在这里插入图片描述

@Entry
@Component
struct Parent {@Provide count: number = 5;build() {Column() {Text(`这是父组件------${this.count}`).width("100%").height(100).backgroundColor(Color.Red)Button("修改count").onClick(() => {this.count++;})Child()}.height("100%").width("100%")}
}@Component
struct Child {build() {Column() {Text(`这是子组件`)GrandSon()}.width("100%").height(200).backgroundColor(Color.Green)}
}@Component
struct GrandSon {@Consume count: number; // 不能设置默认值build() {Column() {Text(`这是孙组件------${this.count}`).width("100%").height(100).backgroundColor(Color.Yellow)}}
}

3.1.3. 使用示例-后代组件修改数据:

在这里插入图片描述

@Entry
@Component
struct Parent {@Provide count: number = 5;build() {Column() {Text(`这是父组件------${this.count}`).width("100%").height(100).backgroundColor(Color.Red)Button("修改count").onClick(() => {this.count++;})Child()}.height("100%").width("100%")}
}@Component
struct Child {build() {Column() {Text(`这是子组件`)GrandSon()}.width("100%").height(200).backgroundColor(Color.Green)}
}@Component
struct GrandSon {@Consume count: number; // 不能设置默认值build() {Column() {Text(`这是孙组件------${this.count}`).width("100%").height(100).backgroundColor(Color.Yellow)Button("修改count").onClick(() => {this.count++;})}}
}

4. @Observed 与 @ObjectLink 嵌套类对象属性变化

上文所述的装饰器仅能观察到第一层的变化,但是在实际应用开发中,应用会根据开发需要,封装自己的数据模型。对于多层嵌套的情况,比如二维数组,或者数组项 class,或者class的属性是 class,他们的第二层的属性变化是无法观察到的。这就引出了@Observed/@ObjectLink 装饰器。

在这里插入图片描述

4.1. 下面这么传递的数据,在子组件中不能修改:

interface BookInfo {id: number;author: string;name: string;pageSize: number;
}class BookItem implements BookInfo {id: number = 0;author: string = "";name: string = "";pageSize: number = 0;constructor(bookInfo: BookInfo) {this.id = bookInfo.id;this.author = bookInfo.author;this.name = bookInfo.name;this.pageSize = bookInfo.pageSize;}
}@Entry
@Component
struct Parent {@State BookArr:BookInfo[] = [new BookItem({id: 1, author: "罗贯中", name: "西游记", pageSize: 20000}),new BookItem({id: 2, author: "施耐庵", name: "水浒传", pageSize: 30000}),new BookItem({id: 3, author: "曹雪芹", name: "红楼梦", pageSize: 20000}),new BookItem({id: 4, author: "吴承恩", name: "三国演义", pageSize: 30000}),]build() {Column() {Text("这是父组件")ForEach(this.BookArr, (item: BookInfo) => {Child({item})})}.width("100%").height("100%")}
}@Component
struct Child {// 非响应式数据传递,需要默认值,否则会报错item:BookInfo = new BookItem({id: 1, author: "罗贯中", name: "西游记", pageSize: 20000});build() {Column() {Text(`${this.item.name}》--------${this.item.author}-------${this.item.pageSize}`)}.width("100%").height(100).backgroundColor(Color.Pink)}
}

4.2. 使用@Observed 和 @ObjectLink:

在这里插入图片描述

  • @ObjectLink 修饰的变量,不能设置默认值,否则会报错。
  • @ObjectLink 修饰的变量,必须用 @Observed 修饰的类作为类型。
interface BookInfo {id: number;author: string;name: string;pageSize: number;
}@Observed
class BookItem implements BookInfo {public id: number = 0;public author: string = "";public name: string = "";public pageSize: number = 0;constructor(bookInfo: BookInfo) {this.id = bookInfo.id;this.author = bookInfo.author;this.name = bookInfo.name;this.pageSize = bookInfo.pageSize;}
}@Entry
@Component
struct Parent {@State BookArr:BookInfo[] = [new BookItem({id: 1, author: "罗贯中", name: "西游记", pageSize: 20000}),new BookItem({id: 2, author: "施耐庵", name: "水浒传", pageSize: 30000}),new BookItem({id: 3, author: "曹雪芹", name: "红楼梦", pageSize: 20000}),new BookItem({id: 4, author: "吴承恩", name: "三国演义", pageSize: 30000}),]build() {Column() {Text(`这是父组件`).width("100%").height(100).backgroundColor(Color.Gray)List({space: 5}) {ForEach(this.BookArr, (item: BookInfo) => {ListItem() {Column() {Text(`这是初始数据:《${item.name}》--------${item.author}-------${item.pageSize}`)Child({item: item})}}})}}.width("100%").height("100%")}
}@Component
struct Child {// @ObjectLink 修饰的变量,不能设置默认值,否则会报错// 注意:此处得 item 必须用 @Observed 修饰的类作为类型@ObjectLink item:BookItem;build() {Column() {Text(`子组件《${this.item.name}》--------${this.item.author}-------${this.item.pageSize}`)Button("改变pageSize的值").onClick(() => {this.item.pageSize++;})}.width("100%").height(100).backgroundColor(Color.Pink)}
}

4.3. 可以发现,当我们在子组件中修改数据时,子组件试图更新了,那么父组件是否更新呢?再看下面这个例子:

在这里插入图片描述
我们在父组件修改数组第一项的数据,结果发现,子组件试图更新了,但是父组件试图却并没有更新:

interface BookInfo {id: number;author: string;name: string;pageSize: number;
}@Observed
class BookItem implements BookInfo {public id: number = 0;public author: string = "";public name: string = "";public pageSize: number = 0;constructor(bookInfo: BookInfo) {this.id = bookInfo.id;this.author = bookInfo.author;this.name = bookInfo.name;this.pageSize = bookInfo.pageSize;}
}@Entry
@Component
struct Parent {@State BookArr:BookInfo[] = [new BookItem({id: 1, author: "罗贯中", name: "西游记", pageSize: 20000}),new BookItem({id: 2, author: "施耐庵", name: "水浒传", pageSize: 30000}),new BookItem({id: 3, author: "曹雪芹", name: "红楼梦", pageSize: 20000}),new BookItem({id: 4, author: "吴承恩", name: "三国演义", pageSize: 30000}),]build() {Column() {Text(`这是父组件`).width("100%").height(100).backgroundColor(Color.Gray)Button("父组件修改原始数据").onClick(() => {this.BookArr[0].pageSize+=10console.log(`${this.BookArr[0].pageSize}`)})List({space: 5}) {ForEach(this.BookArr, (item: BookInfo) => {ListItem() {Column() {Text(`这是初始数据:《${item.name}》--------${item.author}-------${item.pageSize}`)Child({item: item})}}})}}.width("100%").height("100%")}
}@Component
struct Child {// @ObjectLink 修饰的变量,不能设置默认值,否则会报错// 注意:此处得 item 必须用 @Observed 修饰的类作为类型@ObjectLink item:BookItem;build() {Column() {Text(`子组件《${this.item.name}》--------${this.item.author}-------${this.item.pageSize}`)Button("改变pageSize的值").onClick(() => {this.item.pageSize++;})}.width("100%").height(100).backgroundColor(Color.Pink)}
}

将数组第一项数据输入到控制台,发现其实父组件的数组是更新了,只是试图未更新:

在这里插入图片描述

每个装饰器都有自己可以观察的能力,并不是所有的改变都可以被观察到,只有可以被观察到的变化才会进行UI更新。@Observed 装饰器可以观察到嵌套对象的属性变化,其他装饰器仅能观察到第一层的变化。

4.4. 解决 @Observed 与 @ObjectLink 试图更新问题:

多层嵌套的数据更新,子视图会更新,父组件视图不会更新。想让父组件视图更新,可以通过重新赋值的方式实现:
在这里插入图片描述

添加重新赋值的代码:

在这里插入图片描述

// 更新父组件视图
this.BookArr[0] = new BookItem(this.BookArr[0])

5. @Watch 装饰器:状态变量更改通知

@Watch 用于监听状态变量的变化,当状态变量变化时,@Watch 的回调方法将被调用。@WatchArkUI 框架内部判断数值有无更新使用的是严格相等(===),遵循严格相等规范。当在严格相等为 false 的情况下,就会触发 @Watch 的回调。

在这里插入图片描述

在这里插入图片描述

5.1. 使用示例-@Watch和@Prop结合使用

在这里插入图片描述

import promptAction from '@ohos.promptAction';@Entry
@Component
struct Parent {@State @Watch("update") count: number = 5;update() {promptAction.showToast({message: `count值变成了${this.count}`})}build() {Column() {Text(`这是父组件------${this.count}`)Button("修改count").onClick(() => {this.count++;})Child({count: this.count})}.height("100%").width("100%")}
}@Component
struct Child {@Prop count: number = 0;build() {Column() {Text(`这是子组件------${this.count}`)Button("修改count").onClick(() => {this.count++;})}}
}

5.2. 使用示例-@Watch和@Link结合使用

在这里插入图片描述

import promptAction from '@ohos.promptAction';@Entry
@Component
struct Parent {@State @Watch("update") count: number = 5;update() {promptAction.showToast({message: `count值变成了${this.count}`})}build() {Column() {Text(`这是父组件------${this.count}`)Button("修改count").onClick(() => {this.count++;})Child({count: $count}) // 必须使用$}.height("100%").width("100%")}
}@Component
struct Child {@Link count: number; // 不能设置默认值build() {Column() {Text(`这是子组件------${this.count}`)Button("修改count").onClick(() => {this.count++;})}}
}

5.3. 使用示例-@Watch和@Provide结合使用

在这里插入图片描述
使用时,@Watch 放在紧跟 @Provide 之后的位置:

import promptAction from '@ohos.promptAction';@Entry
@Component
struct Parent {@Provide @Watch("update") count: number = 5;update() {promptAction.showToast({message: `count值变成了${this.count}`})}build() {Column() {Text(`这是父组件------${this.count}`).width("100%").height(100).backgroundColor(Color.Red)Button("修改count").onClick(() => {this.count++;})Child()}.height("100%").width("100%")}
}@Component
struct Child {build() {Column() {Text(`这是子组件`)GrandSon()}.width("100%").height(200).backgroundColor(Color.Green)}
}@Component
struct GrandSon {@Consume count: number; // 不能设置默认值build() {Column() {Text(`这是孙组件------${this.count}`).width("100%").height(100).backgroundColor(Color.Yellow)Button("修改count").onClick(() => {this.count++;})}}
}

7. $$语法:内置组件双向同步

$$运算符为系统内置组件提供TS变量的引用,使得TS变量和系统内置组件的内部状态保持同步。

7.1. 使用规则

  • 当前 $$ 支持基础类型变量,以及@State@Link@Prop装饰的变量。
  • 当前 $$ 支持的组件:
    在这里插入图片描述在这里插入图片描述
  • $$ 绑定的变量变化时,会触发UI的同步刷新。

7.2. 使用示例

TextInput 方法的 text 参数为例:
在这里插入图片描述

@Entry
@Component
struct TextInputExample {@State text: string = ''controller: TextInputController = new TextInputController()build() {Column({ space: 20 }) {Text(this.text)TextInput({ text: $$this.text, placeholder: '请输入', controller: this.controller }).placeholderColor(Color.Grey).placeholderFont({ size: 14, weight: 400 }).caretColor(Color.Blue).width(300)}.width('100%').height('100%').justifyContent(FlexAlign.Center)}
}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com