0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

HarmonyOS开发实例:【状态管理】

jf_46214456 来源:jf_46214456 作者:jf_46214456 2024-04-10 09:38 次阅读

ArkUI开发框架提供了多维度的状态管理机制,和UI相关联的数据,不仅可以在组件内使用,还可以在不同组件层级间传递,比如父子组件之间,爷孙组件之间等,也可以是全局范围内的传递,还可以是跨设备传递。另外,从数据的传递形式来看,可以分为只读的单向传递和可变更的双向传递。如下图所示,开发框架提供了多种应用程序状态管理的能力。

2_5_1

@State修饰符

@State 装饰的变量是组件内部的状态数据,当这些状态数据被修改时,将会调用所在组件的 build() 方法刷新UI。 @State 状态数据具有以下特征:

  • 支持多种数据类型:允许 classnumberbooleanstring 强类型的按值和按引用类型。允许这些强类型构成的数组,即ArrayArrayArrayArray。不允许 objectany
  • 内部私有:标记为 @State 的属性是私有变量,只能在组件内访问。
  • 支持多个实例:组件不同实例的内部状态数据独立。
  • 需要本地初始化:必须为所有 @State 变量分配初始值,将变量保持未初始化可能导致框架行为未定义,初始值需要是有意义的值,比如设置 class 类型的值为 null 就是无意义的,会导致编译报错。
  • 创建自定义组件时支持通过状态变量名设置初始值:在创建组件实例时,可以通过变量名显式指定 @State 状态属性的初始值。
    |

简单样例如下所示:

@Entry @Component struct ComponentTest {

      @State date: string = "时间:" + new Date().getTime(); // data变化会触发build方法执行

      build() {
        Column({space: 10}) {

          Text(`父组件【${this.date}】`)                     // 显示时间
            .fontSize(20)
            .backgroundColor(Color.Pink)

          Item()                                            // 子组件
          Item()                                            // 子组件

          Button('更新时间')
            .onClick(() = > {
              this.date = "时间:" + new Date().getTime();   // 点击按钮,date变化,会触发build方法执行
            })
        }
        .width('100%')
        .height('100%')
        .padding(10)
      }
    }

    // 自定义子组件
    @Component struct Item {

      @State time: string = "时间:" + new Date().getTime();

      build() {
        Text(`子组件【${this.time}】`)
          .fontSize(20)
          .backgroundColor(Color.Grey)
          .onClick(() = > {
            this.time = "时间:" + new Date().getTime();     // 点击更新时间,执行build方法
          })
      }
    }

样例运行结果如下图所示:

2_5_1_1

@Prop修饰符

开发应用知识已更新[gitee.com/li-shizhen-skin/harmony-os/blob/master/README.md]参考前往。

@Prop@State 有相同的语义,但初始化方式不同, @Prop 装饰的变量可以和父组件的 @State 变量建立单向的数据绑定。即 @Prop 修饰的变量必须使用其父组件提供的 @State 变量进行初始化,允许组件内部修改 @Prop 变量值但更改不会通知给父组件。 @Prop 状态数据具有以下特征:

  • 支持简单数据类型:仅支持 numberstringboolean 简单类型;

  • 内部私有:标记为 @Prop 的属性是私有变量,只能在组件内访问。

  • 支持多个实例:组件不同实例的内部状态数据独立。

  • 不支持内部初始化:在创建组件的新实例时,必须将值传递给 @Prop 修饰的变量进行初始化,不支持在组件内部进行初始化。
    简单样例如下所示:

    @Entry @Component struct ComponentTest {
    
      @State date: string = "时间:" + new Date().getTime();
    
      build() {
        Column({space: 10}) {
    
          Text(`父组件【${this.date}】`)
            .fontSize(20)
            .backgroundColor(Color.Pink)
    
          Item({time: this.date})                        // 必须初始化子组件的time字段
          Item({time: this.date})                        // 必须初始化子组件的time字段
    
          Button('更新时间')
            .onClick(() = > {
              this.date = "时间:" + new Date().getTime();// 父组件的更改影响子组件
            })
        }
        .width('100%')
        .height('100%')
        .padding(10)
      }
    }
    
    @Component struct Item {
    
      @Prop time: string;                                // 不允许本地初始化
    
      build() {
        Text(`子组件【${this.time}】`)
          .fontSize(20)
          .backgroundColor(Color.Grey)
          .onClick(() = > {
            this.time = "时间:" + new Date().getTime();  // 子组件的更改不影响父组件
          })
      }
    
    }
    

    样例运行结果如下图所示:

    2_5_2_1

@Link修饰符

@Link@State 有相同的语义,但初始化方式不同, @Link 装饰的变量可以和父组件的 @State 变量建立双向的数据绑定。即 @Link 修饰的变量必须使用其父组件提供的 @State 变量进行初始化,允许组件内部修改 @Link 变量值且更改会通知给父组件。 @Link 状态数据具有以下特征:

  • 支持多种数据类型: @Link 变量的值与 @State 变量的类型相同,即 classnumberstringboolean 或这些类型的数组。
  • 内部私有:标记为 @Link 的属性是私有变量,只能在组件内访问。
  • 支持多个实例:组件不同实例的内部状态数据独立。
  • 不支持内部初始化:在创建组件的新实例时,必须将值传递给 @Link 修饰的变量进行初始化,不支持在组件内部进行初始化。初始化使用 $ 符号,例如:$propertiesName。

样例如下:

@Entry @Component struct ComponentTest {

  @State date: string = "时间:" + new Date().getTime(); // 定义@State变量

  build() {
    Column({space: 10}) {

      Text(`父组件【${this.date}】`)
        .fontSize(20)
        .backgroundColor(Color.Pink)

      Item({time: $date})                               // 初始化子组件time属性使用$符号
      Item({time: $date})                               // 初始化子组件time属性使用$符号

      Button('更新时间')
        .onClick(() = > {
          this.date = "时间:" + new Date().getTime();   // 变更date,子组件的对应属性也变化
        })
    }
    .width('100%')
    .height('100%')
    .padding(10)
  }
}

@Component struct Item {

  @Link time: string;

  build() {
    Text(`子组件【${this.time}】`)
      .fontSize(20)
      .backgroundColor(Color.Grey)
      .onClick(() = > {
        this.time = "时间:" + new Date().getTime();     // 变更time,父组件的对应属性也变化
      })
  }
}

样例运行结果如下图所示:

2_5_3_1

@StorageLink修饰符

@StorageLink(key) 装饰的变量是组件内部的状态数据,当这些状态数据被修改时,将会调用所在组件的 build() 方法进行UI刷新。组件通过使用 @StorageLink(key) 装饰的状态变量与 AppStorage 建立双向数据绑定。当创建包含 @StorageLink 的状态变量的组件时,该状态变量的值将使用 AppStorage 中的值进行初始化,在UI组件中对 @StorageLink 的状态变量所做的更改将同步到 AppStorage ,并从 AppStorage 同步到任何其他绑定实例中,如 PersistentStorage 或其他绑定的UI组件。 @StorageLink 状态数据具有以下特征:

  • 支持多种数据类型:支持的数据类型和 @State 一致且支持 object
  • 需要本地初始化:必须为所有 @StorageLink 变量分配初始值。
  • 数据状态全局化:使用 @StorageLink 修饰的数据变化后全局都会改变。
  • 数据持久化:通过搭配 PersistentStorage 接口实现数据持久化。
    • 绑定数据
      简单样例如下所示:
      @Entry @Component struct ComponentTest {
      
        @StorageLink('time') time: string = "1648643734154";// 使用StorageLink标记并初始化
      
        build() {
          Column({space: 10}) {
      
            Text(`父组件【${this.time}】`) // 使用time值
              .fontSize(20)
              .backgroundColor(Color.Pink)
      
            Button('更新时间')
              .onClick(() = > {
                this.time = new Date().getTime().toString();// 更改time的值
              })
          }
          .width('100%')
          .height('100%')
          .padding(10)
        }
      }
      

运行结果如下图所示:

2_5_4_1

-   **双向绑定数据**

    简单样例如下所示:

    ```
    @Entry @Component struct ComponentTest {

      @StorageLink('time') time1: string = "1648643734154";
      @StorageLink('time') time2: string = "abcdefefwefwewee";

      build() {
        Column({space: 10}) {

          Text(`父组件【${this.time1}】`)
            .fontSize(20)
            .backgroundColor(Color.Pink)

          Item();
          Item();

          Button('更新时间')
            .onClick(() = > {
              this.time2 = new Date().getTime().toString();
            })
        }
        .width('100%')
        .height('100%')
        .padding(10)
      }
    }

    @Component struct Item {

      @StorageLink('time') time: string = "OpenHarmony";

      build() {
        Text(`子组件【${this.time}】`)
          .fontSize(20)
          .backgroundColor(Color.Grey)
          .onClick(() = > {
            this.time = new Date().getTime().toString();
          })
      }
    }
    ```

运行结果如下图所示:

2_5_4_2

  • 页面间数据绑定

简单样例如下图所示:


// 第一个页面
@Entry @Component struct ComponentTest {

  @StorageLink('time') time1: string = "1648643734154";// 应用key的值以首次初始化的值为准
  @StorageLink('time') time2: string = "abcdefefwefwewee";// time2以time1的值为准

  build() {
    Column({space: 10}) {

      Text(`父组件【${this.time1}】`)
        .fontSize(20)
        .backgroundColor(Color.Pink)

      Item();// 使用自定义组件
      Item();// 使用自定义组件

      Button('更新时间')
        .onClick(() = > {
          this.time2 = new Date().getTime().toString();// 更改time2的值,所有使用key的页面都会刷新
        })

      Button('跨页面数据绑定')
        .onClick(() = > {
          router.push({uri: "pages/test/setting"})// 打开第二个页面
        })
    }
    .width('100%')
    .height('100%')
    .padding(10)
  }
}

// 自定义个组件
@Component struct Item {

  @StorageLink('time') time: string = "OpenHarmony";// time的值以key第一次出现的初始化为准

  build() {
    Text(`子组件【${this.time}】`)
      .fontSize(20)
      .backgroundColor(Color.Grey)
      .onClick(() = > {
        this.time = new Date().getTime().toString();// 更改time的值,所有使用key的页面都会刷新
      })
  }
}


// 第二个页面
@Entry @Component struct Setting {

  @StorageLink('time') tips: string = "我是第二个页面"; // tips的值以'key'第一次出现的为准

  build() {
    Column({space: 10}) {
      Text(this.tips) // tips的值以'key'第一次出现的为准
        .fontSize(20)
        .margin(20)
        .onClick(() = > {
          this.tips = "0000000000000" // 更改tips的值,所有使用key的页面都会更新
        })

      Button('返回')
        .onClick(() = > {
          router.back()// 点击返回,首页的数据会更改
        })
    }
    .width('100%')
    .height('100%')
  }
}

运行结果如下图所示:

2_5_4_3

  • 持久化数据

@StorageLink 搭配 PersistentStorage 接口可以实现数据本地持久化,简单样例如下图所示:


// 持久化存储key并设置默认值
PersistentStorage.PersistProp("time", "Hello, OpenHarmony")

@Entry @Component struct ComponentTest {

  // 初始化time1,如果AppStorage
  @StorageLink('time') time1: string = "1648643734154";
  @StorageLink('time') time2: string = "OpenHarmony";

  build() {
    Column({space: 10}) {

      Text(`父组件【${this.time1}】`)
        .fontSize(20)
        .backgroundColor(Color.Pink)

      Item();
      Item();

      Button('更新时间')
        .onClick(() = > {
          this.time2 = new Date().getTime().toString();
        })

      Button('跨页面数据绑定')
        .onClick(() = > {
          router.push({uri: "pages/test/setting"})
        })
    }
    .width('100%')
    .height('100%')
    .padding(10)
  }
}

// 自定义组件
@Component struct Item {

  @StorageLink('time') time: string = "OpenHarmony";

  build() {
    Text(`子组件【${this.time}】`)
      .fontSize(20)
      .backgroundColor(Color.Grey)
      .onClick(() = > {
        this.time = new Date().getTime().toString();
      })
  }
}

运行结果如下图所示:

2_5_4_4

@Watch修饰符

@Watch 用来监听状态变量的变化,当它修饰的状态变量发生变更时,回调相应的方式,语法结构为:

@State @Watch("function_name") count : number = 0;

上述语句表示:给状态变量 count 增加一个 @Watch 装饰器,通过 @Watch 注册一个回调方法 function_name , 当状态变量 count 被改变时, 触发 function_name 回调。

简单样例如下所示:

@Entry @Component struct WatchTest {

@State @Watch("onBasketUpdated") shopBasket: Array< number > = [7, 12, 47, 3];
@State totalPurchase: number = 0;

updateTotal(): number {
let sum = 0;
this.shopBasket.forEach((i) = > {
sum += i;
});
// 计算新的购物篮总价值,如果超过100RMB,则适用折扣
this.totalPurchase = (sum < 100) ? sum : 0.9 * sum;
return this.totalPurchase;
}

onBasketUpdated(propName: string): void {
this.updateTotal();
}

build() {
Column({space: 10}) {
Text(`${this.totalPurchase}`)
.fontSize(30)

Button("add to basket")
.onClick(() = > {
this.shopBasket.push(Math.round(100 * Math.random()))
})
}
.width("100%")
.height("100%")
.padding(10)

}
}

样例运行结果如下图所示:

2_5_5_1

集合 shopBasket 是一个状态变量,它被 @Watch 修饰符修饰并绑定了 onBasketUpdated() 方法回调,当点击按钮往 shopBasket 里添加数据时会触发 onBasketUpdated() 方法的调用,该方法里边执行了 totalPurchase 的数据计算,最后页面刷新。

@Watch 装饰器只能监听 @State@Prop@Link@ObjectLink@Provide@Consume@StorageProp 以及 @StorageLink 装饰的变量。

小结

通过对ArkUI三种状态管理的介绍,可以根据具体的业务场景选择不同的状态管理模式。


审核编辑 黄宇

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 鸿蒙
    +关注

    关注

    55

    文章

    1641

    浏览量

    42123
  • HarmonyOS
    +关注

    关注

    79

    文章

    1861

    浏览量

    29267
  • OpenHarmony
    +关注

    关注

    23

    文章

    3322

    浏览量

    15161
收藏 人收藏

    评论

    相关推荐

    HarmonyOS开发实例:【分布式数据管理

    eTS中分布式数据管理的使用,包括KVManager对象实例的创建和KVStore数据流转的使用。
    的头像 发表于 04-11 09:57 447次阅读
    <b class='flag-5'>HarmonyOS</b><b class='flag-5'>开发</b><b class='flag-5'>实例</b>:【分布式数据<b class='flag-5'>管理</b>】

    HarmonyOS开发必备知识

    这篇文档带领大家掌握HarmonyOS开发的一些必备基础知识:1、应用基础知识;2、应用配置文件;3、资源文件;4、应用数据管理;5、应用权限管理注:文档和视频中所有的图片及代码截图皆
    发表于 09-10 17:39

    HarmonyOS应用开发学习路线

    “面向未来”的操作系统到底是什么、能带来什么、有什么特征,看这里:HarmonyOS 概述2.了解HarmonyOS应用开发基础知识HarmonyOS的应用结构、配置文件、资源文件、数
    发表于 09-11 16:14

    HarmonyOS应用开发NFC、蓝牙、WLAN、网络管理、电话服务资料

    和其他设备互联互通。HarmonyOS网络管理模块主要提供以下功能:数据连接管理:网卡绑定,打开URL,数据链路参数查询。数据网络管理:指定数据网络传输,获取数据网络
    发表于 09-21 14:19

    HarmonyOS】应用开发文档

    /basic-fundamentals-0000000000041611快速入门补充该实例在新建工程时需要选择的设备类型和模板,避免开发者选择错误https://developer.harmonyos.com/cn/docs/d
    发表于 10-14 18:04

    绝对干货!HarmonyOS开发者日资料全公开,鸿蒙开发者都在看

    应用开发模板,分布式调试调优,上架开发规范解读等。6、HarmonyOS 分布式应用框架深入解读:该主题深度解析HarmonyOS分布式应用框架,包括系统架构分层,迁移、协同框架及分布
    发表于 08-04 14:36

    【资料】华为HarmonyOS 音频开发管理指南

    华为HarmonyOS 音频开发管理指南回复帖子查看资料下载链接:[hide][/hide]
    发表于 08-12 12:08

    HarmonyOS卡片开发--服务卡片概述

    片服务。 卡片提供方实例管理模块:由卡片提供方开发者实现,负责对卡片管理服务分配的卡片实例进行持久化管理
    发表于 09-22 14:10

    【资料合集】HarmonyOS应用开发的学习路线

    HarmonyOS应用开发基础知识  HarmonyOS的应用结构、配置文件、资源文件、数据管理、权限隐私管理等应用
    发表于 03-14 14:02

    HarmonyOS/OpenHarmony应用开发-FA模型综述

    FA模型整体架构****HarmonyOS用户程序的开发本质上就是开发Ability。HarmonyOS系统是通过对Ability调度,结合系统提供的一致性调度契约对Ability进行
    发表于 12-07 10:39

    HarmonyOS/OpenHarmony应用开发-PageAbility开发体验

    功能简介PageAbility是具备ArkUI实现的Ability,是开发者具体可见并可以交互的Ability实例开发者通过IDE创建Ability时,IDE会自动创建相关模板代码
    发表于 12-08 11:03

    HarmonyOS后台任务管理开发指南上线!

    HarmonyOS 后台任务管理开发指南上新内容,希望能够帮助开发者更高效地探索、体验、上手 HarmonyOS。 同时,我们十分重视
    发表于 11-29 09:58

    华为开发HarmonyOS零基础入门:声明式UI开发

    华为开发HarmonyOS零基础入门:声明式UI开发,用户界面任何状态的改变都只有一种编码途径,完善一些交互体验。
    的头像 发表于 10-23 09:55 1148次阅读
    华为<b class='flag-5'>开发</b>者<b class='flag-5'>HarmonyOS</b>零基础入门:声明式UI<b class='flag-5'>开发</b>

    HarmonyOS开发:舒尔特方格游戏

    为丰富 HarmonyOS 对云端开发的支持、实现 HarmonyOS 生态端云联动,DevEco Studio 推出了云开发功能,开发者在
    的头像 发表于 06-19 15:05 502次阅读
    <b class='flag-5'>HarmonyOS</b>云<b class='flag-5'>开发</b>:舒尔特方格游戏

    开发者说】HarmonyOS实践之应用状态变量共享

    】,即可获得投稿渠道。期待你们的分享~ 平时在开发的过程中,我们会在应用中共享数据,在不同的页面间共享信息。虽然常用的共享信息,也可以通过不同页面中组件间信息共享的方式,但有时使用应用级别的状态管理会让
    的头像 发表于 12-26 21:20 422次阅读
    【<b class='flag-5'>开发</b>者说】<b class='flag-5'>HarmonyOS</b>实践之应用<b class='flag-5'>状态</b>变量共享