当前位置: 首页 > news >正文

鸿蒙进阶-状态管理之@Prop@Link

大家好啊,这里是鸿蒙开天组,今天我们来学习状态管理。

开始组件化开发之后,如何管理组件的状态会变得尤为重要,咱们接下来系统的学习一下这部分的内容

状态管理机制

在声明式UI编程框架中,UI是程序状态的运行结果,用户构建了一个UI模型,其中应用的运行时的状态是参数。当参数改变时,UI作为返回结果,也将进行对应的改变。这些运行时的状态变化所带来的UI的重新渲染,在ArkUI中统称为状态管理机制。

自定义组件拥有变量,变量必须被装饰器装饰才可以成为状态变量,状态变量的改变会引起UI的渲染刷新。如果不使用状态变量,UI只能在初始化时渲染,后续将不会再刷新。 下图展示了State和View(UI)之间的关系。

  • View(UI):UI渲染,指将build方法内的UI描述和@Builder装饰的方法内的UI描述映射到界面。
  • State:状态,指驱动UI更新的数据。用户通过触发组件的事件方法,改变状态数据。状态数据的改变,引起UI的重新渲染。

基本概念

接下来咱们同步一下一些关键词的称呼

  • 状态变量:被状态装饰器装饰的变量,状态变量值的改变会引起UI的渲染更新。示例:@State num: number = 1,其中,@State是状态装饰器,num是状态变量。
  • 常规变量:没有被状态装饰器装饰的变量,通常应用于辅助计算。它的改变永远不会引起UI的刷新。以下示例中increaseBy变量为常规变量。
  • 数据源/同步源:状态变量的原始来源,可以同步给不同的状态数据。通常意义为父组件传给子组件的数据。以下示例中数据源为count: 1。
  • 命名参数机制:父组件通过指定参数传递给子组件的状态变量,为父子传递同步参数的主要手段。示例:CompA: ({ aProp: this.aProp })。
  • 从父组件初始化:父组件使用命名参数机制,将指定参数传递给子组件。子组件初始化的默认值在有父组件传值的情况下,会被覆盖。示例:
@Entry
@Component
struct Parent {build() {Column() {// 从父组件初始化,覆盖本地定义的默认值ChildComponent({ count: 1,})}}
}@Component
struct ChildComponent {// 状态变量,更改会触发 UI 刷新@State count: number = 0;build() {Text(this.count.toString())}
}
  • 初始化子组件:父组件中状态变量可以传递给子组件,初始化子组件对应的状态变量。示例同上。
  • 本地初始化:在变量声明的时候赋值,作为变量的默认值。示例:@State count: number = 0。

装饰器总览

ArkUI提供了多种【装饰器】,通过使用这些装饰器,状态变量不仅可以观察在组件内的改变,还可以在不同组件层级间传递,比如父子组件、跨组件层级,也可以观察全局范围内的变化。根据状态变量的影响范围,将所有的装饰器可以大致分为:

  • 管理组件拥有状态的装饰器:组件级别的状态管理,可以观察组件内变化,和不同组件层级的变化,但需要唯一观察同一个组件树上,即同一个页面内。
  • 管理应用拥有状态的装饰器:应用级别的状态管理,可以观察不同页面,甚至不同UIAbility的状态变化,是应用内全局的状态管理。

下面这张图就是完整的装饰器说明图,咱们后续的学习就围绕着这张图来展开

  1. 管理组件状态:小框中(目前专注这个即可)
  2. 管理应用状态:大框中

@State 自己的状态

@State 装饰器咱们已经学习过了,所以就不从头讲解,而是说2 个使用的注意点

观察变化注意点:

并不是状态变量的所有更改都会引起UI的刷新,只有可以【被框架观察到】的修改才会引起UI刷新。

  • 当装饰的数据类型为boolean、string、number类型时,可以观察到数值的变化。
  • 当装饰的数据类型为class或者Object时,可以观察到自身的赋值的变化,和其属性赋值的变化,即Object.keys(observedObject)返回的所有属性。例子如下。声明ClassA和Model类。
// 基本数据类型
@State count: number = 0;
// 可以观察到数值变化
this.count = 1;//Object.keys 测试interface Chicken {name: stringage: numbercolor: string
}const c: Chicken = {name: '1',age: 2,color: '黄绿色'
}
// 获取对象的属性名,返回字符串数组
console.log('', Object.keys(c))// name,age,color//复杂数据类型且嵌套interface Dog {name: string
}interface Person {name: stringdog: Dog
}@Component
export struct HelloComponent {// 状态变量@State message: string = 'Hello, World!';@State person: Person = {name: 'jack',// 嵌套属性dog: {name: '柯基'}}sayHi() {console.log('你好呀')}build() {Column() {Text(this.message)Button('修改 message').onClick(() => {this.message = 'Hello,ArkTS'})Text(JSON.stringify(this.person))Button('修改title外层属性').onClick(() => {this.person.name = '666'})Button('修改title嵌套属性').onClick(() => {// 修改嵌套属性,无法被监听,UI 不更新this.person.dog.name = '内部的 666'// 修改第一层属性,可以被监听,UI 更新// this.person.dog = {//   name: '阿拉斯加'// }})}}
}

@Prop 父子单向

@Prop 装饰的变量可以和父组件建立单向的同步关系。@Prop 装饰的变量是可变的,但是变化不会同步回其父组件。(不要直接修改 Prop 的值)

@Component
struct SonCom {// 默认值可以省略,不设置为 undefined@Prop xxx:类型='可选默认值' build() {}
}@Entry
@Component// FatherCom 父组件
struct FatherCom {build() {Column() {// 子组件SonCom({xxx:'具体的值'})}}
}

@Link双向同步

使用步骤:

  1. 将父组件的状态属性传递给子组件
  2. 子组件通过@Link修饰即可
  3. 分别测试基础,和复杂类型

基础模板

@Entry
@Component// 父组件
struct KnowledgePage {@State count: number = 0build() {Column() {Text('父组件').fontSize(30)Text(this.count.toString())Button('修改数据').onClick(() => {})SonComponent()}.padding(10).height('100%').backgroundColor('#ccc').width('100%').alignItems(HorizontalAlign.Center).padding({ top: 100 })}
}@Component// 子组件
struct SonComponent {// 编写 UIbuild() {Column({ space: 20 }) {Text('我是子组件').fontSize(20)Column() {Button('修改count').onClick(() => {})}}.backgroundColor('#a6c398').alignItems(HorizontalAlign.Center).width('80%').margin({ top: 100 }).padding(10).borderRadius(10)}
}

参考代码

interface Person {name: stringage: number
}@Entry
@Component// 父组件
struct Page03_Link {@State count: number = 0@State p: Person = {name: 'jack',age: 18,}build() {Column() {Text('父组件').fontSize(30)Text(this.count.toString())Text(JSON.stringify(this.p))Button('修改数据').onClick(() => {this.count--})SonComponent({count: this.count,p: this.p})}.padding(10).height('100%').backgroundColor('#ccc').width('100%').alignItems(HorizontalAlign.Center).padding({ top: 100 })}
}@Component// 子组件
struct SonComponent {// 通过 @Link 来接收@Link count: number@Link p: Person// 编写 UIbuild() {Column({ space: 20 }) {Text('我是子组件').fontSize(20)Text(this.count.toString())Text(JSON.stringify(this.p))Column() {Button('修改count').onClick(() => {this.count++})Button('修改Person').onClick(() => {this.p.age++})}}.backgroundColor('#a6c398').alignItems(HorizontalAlign.Center).width('80%').margin({ top: 100 }).padding(10).borderRadius(10)}
}

好啦,今天的内容就到这里,感谢大家的观看,我们下次再见!


http://www.mrgr.cn/news/77845.html

相关文章:

  • 【MATLAB蓝牙定位代码】三维平面定位设计,通过N个蓝牙锚点实现对未知位置的精准定位
  • vue3(十九)-基础入门之vue-nuxt反向代理
  • springboot配置https,并使用wss
  • 【LeetCode面试150】——219存在重复元素
  • .net6 使用 FreeSpire.XLS 实现 excel 转 pdf - docker 部署
  • 将django+vue项目发布部署到服务器
  • 【老白学 Java】Warship v2.0(三)
  • 增量预训练(Pretrain)样本拼接篇
  • Gate学习(6) 指令学习3
  • WPF异步UI交互功能的实现方法
  • cangjie (仓颉) vscode环境搭建
  • .NET9 - 新功能体验(二)
  • 使用bcc/memleak定位C/C++应用的内存泄露问题
  • #Verilog HDL# 谈谈代码中如何跨层次引用
  • 下载安装Android Studio
  • #Verilog HDL# Verilog中的ifdef/ifndef/else等用法
  • 每日一练:位运算-消失的两个数字
  • CNN—LeNet:从0开始神经网络学习,实战MNIST和CIFAR10~
  • 第三十四篇 MobileNetV1、V2、V3模型解析
  • 【计算机网络】数据链路层
  • 算法(Algorithm)
  • Playwright(Java版) - 7: Playwright 页面对象模型(POM)
  • 使用 Spring Boot 和 GraalVM 的原生镜像
  • win10局域网加密共享设置
  • 《计算力学学报》
  • MCSA --- make coding simple again