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

server/2024/11/29 4:14:04/

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

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

状态管理机制

在声明式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.ppmy.cn/server/145810.html

相关文章

【网络安全】

黑客入侵 什么是黑客入侵? “黑客”是一个外来词,是英语单词hacker的中文音译。最初,“黑客”只是一个褒义词,指的是那些尽力挖掘计算机程序最大潜力的点脑精英,他们讨论软件黑客的技巧和态度,以及共享文化…

【C++】类(三):类的其它特性

7.3 类的其它特性 本节将继续介绍之前章节当中 Sales_data 没有体现出来的类的特性,包括:类型成员、类的成员的类内初始值、可变数据成员、内联成员函数、从成员函数返回*this、如何定义并使用类类型及友元类等。 7.3.1 类成员再探 这部分定义了一对相…

Docker的save和export命令的区别,load和import的区别 笔记241124

Docker的save和export命令的区别,load和import的区别 解说1: Docker的save和export命令,以及load和import命令,在功能和使用场景上存在显著的区别。以下是对这两组命令的详细对比和解释: Docker save和export命令的区别 使用方式和目的&am…

泛型擦除是什么?

泛型擦除(Type Erasure)是Java编译器在编译泛型代码时的一种机制,它的目的是确保泛型能够与JAVA的旧版本(即不支持泛型的版本)兼容。泛型擦除会在编译时移除泛型类型信息,并将泛型类型替换为其非泛型的上限类型(通常是Object) 详细解释 在Java中&#…

Spring 小案例体验创建对象的快感(Java EE 学习笔记05)

我们了解了Spring的特性及功能后,接下来我们利用下面的小案例来体验以下Spring的使用方式。 首先创建项目 打开IDEA,选择new工程,如下图: ​ 然后,指定工程名字为SpringDemo,并且指定工程目录为D盘。配…

【Python爬虫五十个小案例】爬取豆瓣电影Top250

博客主页:小馒头学python 本文专栏: Python爬虫五十个小案例 专栏简介:分享五十个Python爬虫小案例 🪲前言 在这篇博客中,我们将学习如何使用Python爬取豆瓣电影Top250的数据。我们将使用requests库来发送HTTP请求,…

Spring Boot开发实战:从入门到构建高效应用

Spring Boot 是 Java 开发者构建微服务、Web 应用和后端服务的首选框架之一。其凭借开箱即用的特性、大量的自动化配置和灵活的扩展性,极大简化了开发流程。本文将以实战为核心,从基础到高级,全面探讨 Spring Boot 的应用开发。 一、Spring B…

命令行版 postman 之 post 小工具

依赖 curljq post.sh #!/bin/bashBASEhttp://119.119.119.119 METHOD$1 URL$BASE/$2 LOGIN$BASE/login echo $URL token$(curl --silent $LOGIN -H Accept: application/json, text/plain, */* -H Accept-Language: zh-CN,zh;q0.9 -H Connection: keep-alive -H Con…