HarmonyOS NEXT应用开发之@Link装饰器:父子双向同步

子组件中被@Link装饰的变量与其父组件中对应的数据源建立双向数据绑定。

说明:

从API version 9开始,该装饰器支持在ArkTS卡片中使用。

概述

@Link装饰的变量与其父组件中的数据源共享相同的值。

限制条件

  • @Link装饰器不能在@Entry装饰的自定义组件中使用。

装饰器使用规则说明

@Link变量装饰器说明
装饰器参数
同步类型双向同步。
父组件中@State, @StorageLink和@Link 和子组件@Link可以建立双向数据同步,反之亦然。
允许装饰的变量类型Object、class、string、number、boolean、enum类型,以及这些类型的数组。
支持Date类型。
API11及以上支持Map、Set类型。支持类型的场景请参考 观察变化 。
API11及以上支持上述支持类型的联合类型,比如string | number, string | undefined 或者 ClassA | null,示例见 Link支持联合类型实例 。
注意
当使用undefined和null的时候,建议显式指定类型,遵循TypeScipt类型校验,比如:@Link a : string | undefined

支持AkrUI框架定义的联合类型Length、ResourceStr、ResourceColor类型。
类型必须被指定,且和双向绑定状态变量的类型相同。
不支持any。
被装饰变量的初始值无,禁止本地初始化。

变量的传递/访问规则说明

传递/访问说明
从父组件初始化和更新必选。与父组件@State, @StorageLink和@Link 建立双向绑定。允许父组件中@State、@Link、@Prop、@Provide、@Consume、@ObjectLink、@StorageLink、@StorageProp、@LocalStorageLink和@LocalStorageProp装饰变量初始化子组件@Link。
从API version 9开始,@Link子组件从父组件初始化@State的语法为Comp({ aLink: this.aState })。同样Comp({aLink: $aState})也支持。
用于初始化子组件允许,可用于初始化常规变量、@State、@Link、@Prop、@Provide。
是否支持组件外访问私有,只能在所属组件内访问。

图1 初始化规则图示

观察变化和行为表现

观察变化

  • 当装饰的数据类型为boolean、string、number类型时,可以同步观察到数值的变化,示例请参考 简单类型和类对象类型的@Link。

  • 当装饰的数据类型为class或者Object时,可以观察到赋值和属性赋值的变化,即Object.keys(observedObject)返回的所有属性,示例请参考 简单类型和类对象类型的@Link 。

  • 当装饰的对象是array时,可以观察到数组添加、删除、更新数组单元的变化,示例请参考数组类型的@Link。

  • 当装饰的对象是Date时,可以观察到Date整体的赋值,同时可通过调用Date的接口setFullYear, setMonth, setDate, setHours, setMinutes, setSeconds, setMilliseconds, setTime, setUTCFullYear, setUTCMonth, setUTCDate, setUTCHours, setUTCMinutes, setUTCSeconds, setUTCMilliseconds 更新Date的属性。

@Component
struct DateComponent {@Link selectedDate: Date;build() {Column() {Button(`child increase the year by 1`).onClick(() => {this.selectedDate.setFullYear(this.selectedDate.getFullYear() + 1)})Button('child update the new date').margin(10).onClick(() => {this.selectedDate = new Date('2023-09-09')})DatePicker({start: new Date('1970-1-1'),end: new Date('2100-1-1'),selected: this.selectedDate})}}
}@Entry
@Component
struct ParentComponent {@State parentSelectedDate: Date = new Date('2021-08-08');build() {Column() {Button('parent increase the month by 1').margin(10).onClick(() => {this.parentSelectedDate.setMonth(this.parentSelectedDate.getMonth() + 1)})Button('parent update the new date').margin(10).onClick(() => {this.parentSelectedDate = new Date('2023-07-07')})DatePicker({start: new Date('1970-1-1'),end: new Date('2100-1-1'),selected: this.parentSelectedDate})DateComponent({ selectedDate:this.parentSelectedDate })}}
}
  • 当装饰的变量是Map时,可以观察到Map整体的赋值,同时可通过调用Map的接口set, clear, delete 更新Map的值。

  • 当装饰的变量是Set时,可以观察到Set整体的赋值,同时可通过调用Set的接口add, clear, delete 更新Set的值。

框架行为

@Link装饰的变量和其所属的自定义组件共享生命周期。

为了了解@Link变量初始化和更新机制,有必要先了解父组件和拥有@Link变量的子组件的关系,初始渲染和双向更新的流程(以父组件为@State为例)。

  1. 初始渲染:执行父组件的build()函数后将创建子组件的新实例。初始化过程如下:

    1. 必须指定父组件中的@State变量,用于初始化子组件的@Link变量。子组件的@Link变量值与其父组件的数据源变量保持同步(双向数据同步)。
    2. 父组件的@State状态变量包装类通过构造函数传给子组件,子组件的@Link包装类拿到父组件的@State的状态变量后,将当前@Link包装类this指针注册给父组件的@State变量。
  2. @Link的数据源的更新:即父组件中状态变量更新,引起相关子组件的@Link的更新。处理步骤:

    1. 通过初始渲染的步骤可知,子组件@Link包装类把当前this指针注册给父组件。父组件@State变量变更后,会遍历更新所有依赖它的系统组件(elementid)和状态变量(比如@Link包装类)。
    2. 通知@Link包装类更新后,子组件中所有依赖@Link状态变量的系统组件(elementId)都会被通知更新。以此实现父组件对子组件的状态数据同步。
  3. @Link的更新:当子组件中@Link更新后,处理步骤如下(以父组件为@State为例):

    1. @Link更新后,调用父组件的@State包装类的set方法,将更新后的数值同步回父组件。
    2. 子组件@Link和父组件@State分别遍历依赖的系统组件,进行对应的UI的更新。以此实现子组件@Link同步回父组件@State。

使用场景

简单类型和类对象类型的@Link

以下示例中,点击父组件ShufflingContainer中的“Parent View: Set yellowButton”和“Parent View: Set GreenButton”,可以从父组件将变化同步给子组件。

1.点击子组件GreenButton和YellowButton中的Button,子组件会发生相应变化,将变化同步给父组件。因为@Link是双向同步,会将变化同步给@State。

2.当点击父组件ShufflingContainer中的Button时,@State变化,也会同步给@Link,子组件也会发生对应的刷新。

class GreenButtonState {width: number = 0;constructor(width: number) {this.width = width;}
}@Component
struct GreenButton {@Link greenButtonState: GreenButtonState;build() {Button('Green Button').width(this.greenButtonState.width).height(40).backgroundColor('#64bb5c').fontColor('#FFFFFF,90%').onClick(() => {if (this.greenButtonState.width < 700) {// 更新class的属性,变化可以被观察到同步回父组件this.greenButtonState.width += 60;} else {// 更新class,变化可以被观察到同步回父组件this.greenButtonState = new GreenButtonState(180);}})}
}@Component
struct YellowButton {@Link yellowButtonState: number;build() {Button('Yellow Button').width(this.yellowButtonState).height(40).backgroundColor('#f7ce00').fontColor('#FFFFFF,90%').onClick(() => {// 子组件的简单类型可以同步回父组件this.yellowButtonState += 40.0;})}
}@Entry
@Component
struct ShufflingContainer {@State greenButtonState: GreenButtonState = new GreenButtonState(180);@State yellowButtonProp: number = 180;build() {Column() {Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center }) {// 简单类型从父组件@State向子组件@Link数据同步Button('Parent View: Set yellowButton').width(312).height(40).margin(12).fontColor('#FFFFFF,90%').onClick(() => {this.yellowButtonProp = (this.yellowButtonProp < 700) ? this.yellowButtonProp + 40 : 100;})// class类型从父组件@State向子组件@Link数据同步Button('Parent View: Set GreenButton').width(312).height(40).margin(12).fontColor('#FFFFFF,90%').onClick(() => {this.greenButtonState.width = (this.greenButtonState.width < 700) ? this.greenButtonState.width + 100 : 100;})// class类型初始化@LinkGreenButton({ greenButtonState: $greenButtonState }).margin(12)// 简单类型初始化@LinkYellowButton({ yellowButtonState: $yellowButtonProp }).margin(12)}}}
}

数组类型的@Link

@Component
struct Child {@Link items: number[];build() {Column() {Button(`Button1: push`).margin(12).width(312).height(40).fontColor('#FFFFFF,90%').onClick(() => {this.items.push(this.items.length + 1);})Button(`Button2: replace whole item`).margin(12).width(312).height(40).fontColor('#FFFFFF,90%').onClick(() => {this.items = [100, 200, 300];})}}
}@Entry
@Component
struct Parent {@State arr: number[] = [1, 2, 3];build() {Column() {Child({ items: $arr }).margin(12)ForEach(this.arr,(item: number) => {Button(`${item}`).margin(12).width(312).height(40).backgroundColor('#11a2a2a2').fontColor('#e6000000')},(item: ForEachInterface) => item.toString())}}
}

上文所述,ArkUI框架可以观察到数组元素的添加,删除和替换。在该示例中@State和@Link的类型是相同的number[],不允许将@Link定义成number类型(@Link item : number),并在父组件中用@State数组中每个数据项创建子组件。如果要使用这个场景,可以参考 @Prop 和@Observed。

装饰Map类型变量

说明:
从API version 11开始,@Link支持Map类型。

在下面的示例中,value类型为Map<number, string>,点击Button改变message的值,视图会随之刷新。

@Component
struct Child {@Link value: Map<number, string>build() {Column() {ForEach(Array.from(this.value.entries()), (item: [number, string]) => {Text(`${item[0]}`).fontSize(30)Text(`${item[1]}`).fontSize(30)Divider()})Button('child init map').onClick(() => {this.value = new Map([[0, "a"], [1, "b"], [3, "c"]])})Button('child set new one').onClick(() => {this.value.set(4, "d")})Button('child clear').onClick(() => {this.value.clear()})Button('child replace the first one').onClick(() => {this.value.set(0, "aa")})Button('child delete the first one').onClick(() => {this.value.delete(0)})}}
}@Entry
@Component
struct MapSample2 {@State message: Map<number, string> = new Map([[0, "a"], [1, "b"], [3, "c"]])build() {Row() {Column() {Child({ value: this.message })}.width('100%')}.height('100%')}
}

装饰Set类型变量

说明:

从API version 11开始,@Link支持Set类型。

在下面的示例中,message类型为Set<number>,点击Button改变message的值,视图会随之刷新。

@Component
struct Child {@Link message: Set<number>build() {Column() {ForEach(Array.from(this.message.entries()), (item: [number, string]) => {Text(`${item[0]}`).fontSize(30)Divider()})Button('init set').onClick(() => {this.message = new Set([0, 1, 2, 3, 4])})Button('set new one').onClick(() => {this.message.add(5)})Button('clear').onClick(() => {this.message.clear()})Button('delete the first one').onClick(() => {this.message.delete(0)})}.width('100%')}
}@Entry
@Component
struct SetSample1 {@State message: Set<number> = new Set([0, 1, 2, 3, 4])build() {Row() {Column() {Child({ message: this.message })}.width('100%')}.height('100%')}
}

Link支持联合类型实例

@Link支持联合类型和undefined和null,在下面的示例中,name类型为string | undefined,点击父组件Index中的Button改变name的属性或者类型,Child中也会对应刷新。

@Component
struct Child {@Link name: string | undefinedbuild() {Column() {Button('Child change name to Bob').onClick(() => {this.name = "Bob"})Button('Child change animal to undefined').onClick(() => {this.name = undefined})}.width('100%')}
}@Entry
@Component
struct Index {@State name: string | undefined = "mary"build() {Column() {Text(`The name is  ${this.name}`).fontSize(30)Child({ name: this.name })Button('Parents change name to Peter').onClick(() => {this.name = "Peter"})Button('Parents change name to undefined').onClick(() => {this.name = undefined})}}
}

常见问题

@Link装饰状态变量类型错误

在子组件中使用@Link装饰状态变量需要保证该变量与数据源类型完全相同,且该数据源需为被诸如@State等装饰器装饰的状态变量。

【反例】

@Observed
class ClassA {public c: number = 0;constructor(c: number) {this.c = c;}
}@Component
struct LinkChild {@Link testNum: number;build() {Text(`LinkChild testNum ${this.testNum}`)}
}@Entry
@Component
struct Parent {@State testNum: ClassA[] = [new ClassA(1)];build() {Column() {Text(`Parent testNum ${this.testNum[0].c}`).onClick(() => {this.testNum[0].c += 1;})// @Link装饰的变量需要和数据源@State类型一致LinkChild({ testNum: this.testNum[0].c })}}
}

@Link testNum: number从父组件的LinkChild({testNum:this.testNum.c})初始化。@Link的数据源必须是装饰器装饰的状态变量,简而言之,@Link装饰的数据必须和数据源类型相同,比如@Link: T和@State : T。所以,这里应该改为@Link testNum: ClassA,从父组件初始化的方式为LinkChild({testNum: $testNum})

【正例】

@Observed
class ClassA {public c: number = 0;constructor(c: number) {this.c = c;}
}@Component
struct LinkChild {@Link testNum: ClassA[];build() {Text(`LinkChild testNum ${this.testNum[0]?.c}`)}
}@Entry
@Component
struct Parent {@State testNum: ClassA[] = [new ClassA(1)];build() {Column() {Text(`Parent testNum ${this.testNum[0].c}`).onClick(() => {this.testNum[0].c += 1;})// @Link装饰的变量需要和数据源@State类型一致LinkChild({ testNum: $testNum })}}
}

为了能让大家更好的学习鸿蒙(HarmonyOS NEXT)开发技术,这边特意整理了《鸿蒙开发学习手册》(共计890页),希望对大家有所帮助:https://qr21.cn/FV7h05

《鸿蒙开发学习手册》:

如何快速入门:https://qr21.cn/FV7h05

  1. 基本概念
  2. 构建第一个ArkTS应用
  3. ……

开发基础知识:https://qr21.cn/FV7h05

  1. 应用基础知识
  2. 配置文件
  3. 应用数据管理
  4. 应用安全管理
  5. 应用隐私保护
  6. 三方应用调用管控机制
  7. 资源分类与访问
  8. 学习ArkTS语言
  9. ……

基于ArkTS 开发:https://qr21.cn/FV7h05

  1. Ability开发
  2. UI开发
  3. 公共事件与通知
  4. 窗口管理
  5. 媒体
  6. 安全
  7. 网络与链接
  8. 电话服务
  9. 数据管理
  10. 后台任务(Background Task)管理
  11. 设备管理
  12. 设备使用信息统计
  13. DFX
  14. 国际化开发
  15. 折叠屏系列
  16. ……

鸿蒙开发面试真题(含参考答案):https://qr18.cn/F781PH

鸿蒙开发面试大盘集篇(共计319页):https://qr18.cn/F781PH

1.项目开发必备面试题
2.性能优化方向
3.架构方向
4.鸿蒙开发系统底层方向
5.鸿蒙音视频开发方向
6.鸿蒙车载开发方向
7.鸿蒙南向开发方向

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/800651.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java常用API_时间

一&#xff0c;JDK7时间&#xff1a; 1.Date&#xff1a; 我先通过一段代码简单展示一下它的几个方法及功能 代码&#xff1a; 这里要注意的是 时间原点&#xff1a;1970年1月1日 00:00:00 中国的时间原点&#xff1a;由于中国处在东八区&#xff0c;时间原点要晚上8小时&…

MySQL 主从 AUTO_INCREMENT 不一致问题分析

本文介绍了 MySQL5.7 中常见的replace into 操作造成的主从auto_increment不一致现象&#xff0c;一旦触发了主从切换&#xff0c;业务的正常插入操作会触发主键冲突的报错提示。 一、问题描述 1.1 问题现象 在 MySQL 5.7 版本中&#xff0c;REPLACE INTO 操作在表存在自增主键…

状态机编程的常用框架和方法

状态机编程是一种管理复杂系统行为的有效方法&#xff0c;它通过定义一系列状态、事件、转换和动作来描述系统的行为。在嵌入式系统开发中&#xff0c;状态机编程尤为重要&#xff0c;因为它可以帮助开发者以结构化的方式处理系统中的并发和异步事件。以下是一些常用的状态机编…

大话设计模式——六大基本设计原则(SOLID原则)

设计模式 定义&#xff1a;软件开发中&#xff0c;在特定上下文中解决一类常见问题的被证明为有效的最佳实践。可供其他开发者重复使用解决相似问题。 好处&#xff1a; 提高代码的可重用性&#xff0c;减少重复代码。提高代码的可维护性&#xff0c;使代码更易于理解和修改。…

django怎么设置把logger.info的日志保存到本地文件

在Django项目中,要将logger.info()产生的日志信息保存到本地文件,需要对Django的logging配置进行设置。以下是一个详细的步骤说明和示例代码:步骤一:配置settings.py在Django项目的settings.py文件中,添加或更新LOGGING配置字典。这是一个标准的Python字典,遵循logging模…

nssm 工具把asp.net core mvc变成 windows服务,使用nginx反向代理访问

nssm工具的作用&#xff1a;把项目部署成Windows服务&#xff0c;可以在系统后台运行 1.创建一个asp.net core mvc的项目weblication1 asp.net core mvc项目要成为windows服务需要安装下面的nuget包 <ItemGroup><PackageReference Include"Microsoft.Extension…

Gradle系列(五)-常用的gradle命令

Gradle系列(五)-常用的gradle命令 本文主要记录下gradle下常用的命令 1: gradle -v gradle -v 命令可以获取Gradle的版本信息。 2: gradle dependencies gradle dependencies命令用于显示项目的依赖关系。它会列出项目中所有的依赖项&#xff0c;包括直接依赖和传递依赖. …

【LeetCode热题100】35. 搜索插入位置(二分)

一.题目要求 给定一个排序数组和一个目标值&#xff0c;在数组中找到目标值&#xff0c;并返回其索引。如果目标值不存在于数组中&#xff0c;返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 二.题目难度 简单 三.输入样例 示例 1: 输入: nums…

PgSQL的with as语法

returning 返回的这一些字段&#xff0c;然后进行汇总为remove_alarms 然后select一下remove_alarms 出来的数据然后保存到tb_alarm_his 里面 with remove_alarms as( delete fromtb_alarm whereid in (508) returning 0,now(),admin,alarmadvice,alarmadvicecn,alarmarises…

2_7.Linux中的无人值守安装脚本kickstart

## 一.kickstart自动安装脚本的作用 ## #在企业中安装多台操作系统时面临的问题# 当安装Linux操作系统时&#xff0c;安装过程会需要回答很多关于设定的问题 这些问题必须手动选择&#xff0c;否则无法进行安装 当只安装1台Linux系统&#xff0c;手动选择设定工作量比较轻松 当…

006 spring aop(初识)

文章目录 pom.xmlLogAspect.javaStudentServiceImpl.javaStudentService.javaapplicationContext.xmlStudentServiceImplTest.java pom.xml <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"…

Java内部类(一)

常用类&#xff08;一&#xff09; 内部类 成员内部类&#xff1a;一个类定义在另一个类中静态内部类&#xff1a;static局部内部类&#xff1a;一个方法内定义一个类匿名内部类&#xff1a;没有名字的内部类 内部类如下 class Outer{class Inner{} }特点&#xff1a; 编译…

『VUE』14. Style绑定(详细图文注释)

目录 行内css动态样式对象引入数组引入代码演示总结 欢迎关注 『VUE』 专栏&#xff0c;持续更新中 欢迎关注 『VUE』 专栏&#xff0c;持续更新中 大体上和前面的class绑定是一致的,只是class换成了style. 请注意,实际开发中,我们一般建议用class,因为style的权重太高了,用sty…

day13 java final 类和对象的初始化执行顺序

final [面试题]请简述final关键字final修饰类&#xff08;最终的类&#xff09;-太监类&#xff1a;该类不能被继承。(比如&#xff1a;String StringBuilder,....) final修饰方法&#xff08;最终的方法&#xff09;&#xff1a;不能被重写 final修饰的变量 &#xff1a;值不…

边缘计算采集网关如何助力制造企业解决数采问题-天拓四方

一、企业背景 某大型制造企业&#xff0c;位于国内某经济发达的工业园区内&#xff0c;拥有多个生产线和智能化设备&#xff0c;致力于提高生产效率、降低运营成本。随着企业规模的扩大和生产自动化的推进&#xff0c;该企业面临着海量数据处理、实时响应和网络安全等多重挑战…

C++(10): std::map、std::multimap与std::unordered_map

1. 简述 std::map、std::multimap 和 std::unordered_map 是 C 标准模板库&#xff08;STL&#xff09;中的三种关联容器&#xff0c;它们提供了存储键值对&#xff08;pair&#xff09;的方式&#xff0c;并允许通过键来快速检索值。这三者之间的主要区别在于它们的内部数据结…

JAVA实现网络爬虫

在Java中实现网络爬虫&#xff0c;可以使用Java内置的java.net.URL和java.net.URLConnection类来发送HTTP请求&#xff0c;也可以使用更高级的库&#xff0c;如Apache的HttpClient和Jsoup&#xff0c;后者可以方便地解析HTML文档。 以下是一个使用HttpClient和Jsoup的Java网络…

day14 java接口

接口 接口&#xff1a;对不同类型不同事物相同功能的描述&#xff08;一定程序上解决了单继承的局限性&#xff09;接口可以理解成一种标准 规范当类实现这个接口就实现了这个标准或规范接口的格式&#xff1a;权限修饰符 interface 接口名{}权限修饰符只能是public和缺省的。…

ElementUI使用问题记录:设置路由+iconfont图标+自定义表单验证

一、关于导航怎么设置路由 1、在el-menu这个标签的属性中添加 router ,官方文档的解释是&#xff1a;启用vue-router 这种模式 2、在el-menu-item标签中的index属性直接书写路由&#xff0c;就可以实现正常vue-router了 3、在el-menu-item标签中书写路由属性&#xff1a;:route…

【C++进阶】哈希的应用之位图和布隆过滤器

位图和布隆过滤器 一&#xff0c;位图1. 实现2. 位图的应用 二&#xff0c;布隆过滤器1. 使用场景2. 模拟实现 三&#xff0c;海量数据面试题哈希切分 四&#xff0c;总结 这一节我们来看哈希的应用 一&#xff0c;位图 先来看一个面试题 这里如果用unordered_set来解决&…