HarmonyOS第七章:应用状态共享(PersistentStorage、LocalStorage、AppStorage)

news/2024/9/28 4:00:46/

🎉 博客主页:【剑九_六千里-CSDN博客】【剑九_六千里-掘金社区】
🎨 上一篇文章:【HarmonyOS第六章:组件状态共享(父子组件传参、多层级组件传参、@Watch监听状态变化、@Observed与@ObjectLink、多层嵌套数据更新)】
🎠 系列专栏:【HarmonyOS系列】
💖 感谢大家点赞👍收藏⭐评论✍

在这里插入图片描述

在这里插入图片描述

文章目录

  • 1. PersistentStorage
    • 1.1. 简单数据类型的持久化,获取和修改
      • 1.1.1. 语法
      • 1.1.2. 使用示例
    • 1.2. 复杂数据类型的持久化,获取和修改
      • 1.2.1. 基本使用
      • 1.2.2. 使用 AppStorage.get() 获取数据,AppStorage.set()写入数据
      • 1.2.3. 使用 link 写入/获取数据
      • 1.3. 在其它页面获取存储的数据
  • 2. LocalStorage
    • 2.1. 基本使用
    • 2.2. 页面内共享
      • 2.2.1. 声明
      • 2.2.2. 接收
      • 2.2.3. 完整示例
    • 2.3. 不同页面间共享
    • 2.4. LocalStorage 存储的数据并不会持久化存储
  • 3. AppStorage
    • 3.1. 基础用法
    • 3.2. 组件共享
    • 3.3. set() 及 get() 方法
    • 3.4. AppStorage.link("user");
  • 4. 总结

1. PersistentStorage

PersistentStorage 允许将选定的 AppStorage 属性保留在设备磁盘上,以便在应用程序关闭后仍然保持持久化。以下是关于PersistentStorage 的一些细节和注意事项:

  • UI 和业务逻辑不应直接访问 PersistentStorage 中的属性,所有属性访问都应通过 AppStorage 进行。任何在 AppStorage 中的更改都会自动同步到 PersistentStorage
  • PersistentStorage 支持存储简单类型数据,如数字(number)、字符串(string)、布尔(boolean)、枚举(enum)等。
  • 如果需要存储对象类型的数据,可以将其转换为 JSON 字符串后再存储。
  • 注意,持久化变量最好是小于2KB的数据。如果开发者需要存储大量的数据,建议使用数据库API来进行管理和存储。

1.1. 简单数据类型的持久化,获取和修改

1.1.1. 语法

// 1. 通过 PersistentStorage.persistProp() 定义要存储的数据
// 参数一:存储的属性;参数二:存储的值
PersistentStorage.persistProp("xxx", xxx)// 2. 通过 @StorageProp() 或 @StorageLink() 接收数据
@StorageProp("xxx")
@StorageLink("xxx")// 3. 接收数据
xxx: number = 0;// 4. 通过 this.xxx 使用数据
this.xxx 

1.1.2. 使用示例

在这里插入图片描述

使用 PersistentStorage 存储数据,使用 @StorageLink@StorageLink 接收数据,同时数据支持修改:

// PersistentStorage 写入磁盘-app关闭数据依然存在,持久化存储
// 存储简单数据类型:number string boolean等等
// 存储负载数据类型:array object function
PersistentStorage.PersistProp("num",100)@Entry
@Component
struct Index {@StorageLink("num") // 获取数据,单向数据流,只是组件内可以改变数据,并不会写入 PersistentStorage// @StorageLink("num") // 获取数据,双向数据流,本地修改会同步写入到 PersistentStoragenum:number=0 // 如果未存储,默认初始值build() {Column({space:10}) {Text(`${this.num}`)Button("修改age").onClick(()=>{this.num++})}.width('100%').height('100%')}
}

1.2. 复杂数据类型的持久化,获取和修改

1.2.1. 基本使用

在这里插入图片描述

  • 注意:存储复杂类型数据时,value 必须是json
// 注意:存储复杂类型数据时,value 必须是json
PersistentStorage.persistProp("user", JSON.stringify({name: "张三", age: 18}))
interface User {name: string;age: number;
}@Entry
@Component
struct Index {@StorageLink("user") // 获取数据 @StorageLink("user") 或 @StorageProp("user")userInfo: string = "{}" // 默认值@State user: User = JSON.parse(this.userInfo); // 将接收到的json转成objectbuild() {Column() {Text(`${this.user.name}--${this.user.age}`) // 使用传入的数据Button("修改Age").onClick(() => {this.user.age++;})}.width("100%").height("100%")}
}

1.2.2. 使用 AppStorage.get() 获取数据,AppStorage.set()写入数据

这个例子中使用 @StorageLink("user") 获取数据,可以实现需求,同时我们也可以使用 AppStorage.get() 来获取数据:
在这里插入图片描述

// 注意:存储复杂类型数据时,value 必须是json
PersistentStorage.persistProp("user", JSON.stringify({name: "张三", age: 18}))
interface User {name: string;age: number;
}@Entry
@Component
struct Index {@StorageLink("user")userInfo: string = "{}"@State user: User = JSON.parse(this.userInfo); // 将接收到的json转成objectbuild() {Column() {Text(`${this.user.name}--${this.user.age}`) // 使用传入的数据Button("修改Age").onClick(() => {this.user.age++;})Button("获取user").onClick(() => {const res = AppStorage.get<string>("user") // 获取到的是原数据console.log(res)})}.width("100%").height("100%")}
}

此时点击修改Age按钮,可以发现age被改变了,但 AppStorage.get() 获取到的是改变前的数据,此时可以通过 AppStorage.set() 重新设置数据 :
在这里插入图片描述

// 注意:存储复杂类型数据时,value 必须是json
PersistentStorage.persistProp("user", JSON.stringify({name: "张三", age: 18}))
interface User {name: string;age: number;
}@Entry
@Component
struct Index {@StorageLink("user")userInfo: string = "{}"@State user: User = JSON.parse(this.userInfo); // 将接收到的json转成objectbuild() {Column() {Text(`${this.user.name}--${this.user.age}`) // 使用传入的数据Button("修改Age").onClick(() => {this.user.age++;AppStorage.set<string>("user", JSON.stringify(this.user)); // 将修改后的数据重新存储})Button("获取user").onClick(() => {const res = AppStorage.get<string>("user")console.log(res)})}.width("100%").height("100%")}
}

1.2.3. 使用 link 写入/获取数据

在这里插入图片描述

// 注意:存储复杂类型数据时,value 必须是jsonPersistentStorage.persistProp("user", JSON.stringify({name: "张三", age: 18}))
interface User {name: string;age: number;
}@Entry
@Component
struct Index {@StorageLink("user")@Watch("update")userInfo: string = "{}"@State user: User = JSON.parse(this.userInfo); // 将接收到的json转成objectupdate() {this.user = JSON.parse(this.userInfo)}build() {Column() {Text(`${this.user.name}--${this.user.age}`) // 使用传入的数据Button("修改Age").onClick(() => {this.user.age++;// 将修改后的数据重新存储AppStorage.set<string>("user", JSON.stringify(this.user));})Button("AppStorage.get获取user").onClick(() => {// 获取数据const res = AppStorage.get<string>("user")console.log(res)})Button("Link 方式 获取/写入user").onClick(() => {// 获取数据const userLink: SubscribedAbstractProperty<string> = AppStorage.link("user")console.log(userLink.get())userLink.set(JSON.stringify({name: "李四", age: 21}))})}.width("100%").height("100%")}
}

1.3. 在其它页面获取存储的数据

测试步骤如下:

  • 修改主页面 age;
  • 点击跳转页面,跳转到其它页面;
  • 查看其它页面获取的数据是否正确;
  • 修改其它页面的数据;
  • 返回主页面,查看主页面数据是否同步更新;

通过 router 跳转页面:

// 注意:存储复杂类型数据时,value 必须是json
import router from '@ohos.router'; // 导入路由模块PersistentStorage.persistProp("user", JSON.stringify({name: "张三", age: 18}))
interface User {name: string;age: number;
}@Entry
@Component
struct PersistentStorage3 {@StorageLink("user")@Watch("update")userInfo: string = "{}"@State user: User = JSON.parse(this.userInfo); // 将接收到的json转成objectupdate() {this.user = JSON.parse(this.userInfo)}build() {Column() {Text(`${this.user.name}--${this.user.age}`) // 使用传入的数据Button("修改Age").onClick(() => {this.user.age++;// 将修改后的数据重新存储AppStorage.set<string>("user", JSON.stringify(this.user));})Button("AppStorage.get获取user").onClick(() => {// 获取数据const res = AppStorage.get<string>("user")console.log(res)})Button("Link 方式 获取/写入user").onClick(() => {// 获取数据const userLink: SubscribedAbstractProperty<string> = AppStorage.link("user")console.log(userLink.get())userLink.set(JSON.stringify({name: "李四", age: 21}))})Button("跳转页面").onClick(() => {// 路由跳转router.pushUrl({url: "pages/PersistentStorage4"})})}.width("100%").height("100%")}
}

路由跳转要求页面必须在 src/main/resources/base/profile/main_pages.json 中进行配置:在这里插入图片描述

PersistentStorage3 页面初始数据为 18 :

在这里插入图片描述

点击 PersistentStorage3 修改Age,并点击跳转到 PersistentStorage4 页面,可以看到 PersistentStorage4 页面获取到了更新后的数据:

在这里插入图片描述
PersistentStorage4 页面修改Age:
在这里插入图片描述

返回 PersistentStorage3 页面,可以看到数据也更新了:
在这里插入图片描述
在这里插入图片描述
因此,使用 PersistentStorage.persistProp() 存储的数据,在所有页面中都可以使用并修改。

2. LocalStorage

LocalStorage 是用于页面级的UI状态存储,通过 @Entry 装饰器接收的参数可以在同一页面内共享相同的LocalStorage 实例。此外,LocalStorage 也可以在 UIAbility 内,在不同页面之间共享状态数据。

2.1. 基本使用

2.2. 页面内共享

在这里插入图片描述

2.2.1. 声明

使用 const storage = new LocalStorage({ key: value }) 来创建LocalStorage实例,其中 key 是存储的键名,value 是初始值。

interface User {name: string;age: number;
}const user: User = { name: '张三', age: 18 };
const storage = new LocalStorage(user);

2.2.2. 接收

  • @Entry 构造器接收一个参数,该参数是通过 const storage = new LocalStorage(user); 存储的数据
@Entry(storage)
  • 单向数据绑定:使用 @LocalStorageProp('user') 装饰器,可以在组件内对LocalStorage的值进行单向修改,仅在组件内生效。
  • 双向数据绑定:使用 @LocalStorageLink('user') 装饰器,可以实现全局范围内对LocalStorage的值进行双向修改,多个组件可以共享相同的LocalStorage实例。
// @LocalStorageProp("user")
@LocalStorageLink("user")

2.2.3. 完整示例

// 非持久化,只能在一个 UIAbility 中使用
interface User {name: string;age: number;
}const user: User = { name: '张三', age: 18 };
const storage = new LocalStorage(user);@Entry(storage)
@Component
struct Index {// @LocalStorageProp("user") // 使用@LocalStorageProp("user")时,修改值,子组件不会同步@LocalStorageLink("user") // 使用@LocalStorageLink("user")时,修改值,子组件会同步userInfo: User = { name: "李四", age: 18 } // 默认值build() {Column() {Text(`${this.userInfo.name}---${this.userInfo.age}`)Button("Index修改age").onClick(() => {this.userInfo.age++;})ChildA()ChildB()}.width('100%')}
}@Component
struct ChildA {@LocalStorageProp("user")userInfo: User = { name: "Tom", age: 20 }build() {Column({ space: 10 }) {Text(`${this.userInfo.name}---${this.userInfo.age}`).fontColor(Color.White)Button("ChildA修改age").onClick(() => {// @LocalStorageProp("user") 接收的参数,子组件修改时,父组件不会同步this.userInfo.age++})}.width('100%').height(100).backgroundColor(Color.Blue)}
}@Component
struct ChildB {@LocalStorageLink("user")userInfo: User = { name: "Tom", age: 20 }build() {Column({ space: 10 }) {Text(`${this.userInfo.name}---${this.userInfo.age}`).fontColor(Color.White)Button("ChildB修改age").onClick(() => {// @LocalStorageLink("user") 接收的参数,子组件修改时,父组件会同步this.userInfo.age++})}.width('100%').height(100).backgroundColor(Color.Brown)}
}

2.3. 不同页面间共享

  • 创建一个 IndexOther.ets 文件
    在这里插入图片描述

  • IndexOther.ets 加入到路由中
    在这里插入图片描述

  • UIAbility中创建LocalStorage:可以在UIAbility中创建LocalStorage实例,并通过 loadContent 方法提供给加载的窗口,以便在页面间共享状态数据。
    在这里插入图片描述

  • 在页面中获取LocalStorage实例:在页面中,可以通过 const storage = LocalStorage.getShared() 来获取LocalStorage的实例。然后,可以通过 @Entry(storage) 将实例传递给页面,以便在页面中使用LocalStorage来共享和管理状态数据。

// Index.etsimport router from "@ohos.router";
interface User {name: string;age: number;
}
const storage = LocalStorage.getShared();@Entry(storage)
@Component
struct Index {@LocalStorageLink("user")userInfo: User = { name: "李四", age: 18 } // 默认值build() {Column() {Text("这是Index页面")Text(`${this.userInfo.name}---${this.userInfo.age}`)Button("Index修改age").onClick(() => {this.userInfo.age++;})Button("跳转到IndexOther页面").onClick(() => {router.pushUrl({url: "pages/IndexOther"})})}.width('100%')}
}// IndexOther.ets
import router from "@ohos.router";// 非持久化,只能在一个 UIAbility 中使用
interface UserInfo {name: string;age: number;
}const storage = LocalStorage.getShared();@Entry(storage)
@Component
struct IndexOther {@LocalStorageLink("user")userInfo: UserInfo = { name: "李四", age: 18 } // 默认值build() {Column() {Text("这是IndexOther页面")Text(`${this.userInfo.name}---${this.userInfo.age}`)Button("Index修改age").onClick(() => {this.userInfo.age++;})Button("跳转到Index页面").onClick(() => {router.pushUrl({url: "pages/Index"})})}.width('100%')}
}

此时不论在哪一个页面修改数据,都会同步:
在这里插入图片描述

2.4. LocalStorage 存储的数据并不会持久化存储

打开模拟机:

在这里插入图片描述
在这里插入图片描述
初始值如下:

在这里插入图片描述
修改数据:

在这里插入图片描述
关闭当前页面:

在这里插入图片描述
再次打开后发现数据已经变成了初始值,因此 LocalStorage 存储的数据并不会持久化存储:

在这里插入图片描述

3. AppStorage

AppStorage 是应用程序级别的全局UI状态存储,与应用程序的进程绑定。它由UI框架在应用程序启动时创建,用于中央存储应用程序的UI状态属性。

3.1. 基础用法

  • 初始化使用:可以使用 AppStorage.SetOrCreate(key, value) 来初始化AppStorage中的数据,其中 key 是存储的键名,value 是初始值。
  • 单向数据绑定:使用 @StorageProp('user') 装饰器,可以在组件内对AppStorage的值进行单向修改,仅在组件内生效。
  • 双向数据绑定:使用 @StorageLink('user') 装饰器,可以实现全局范围内对AppStorage的值进行双向修改,多个组件可以共享相同的AppStorage实例,从而实现全局状态管理。
interface Person {name: string;age: number;
}const user: Person = { name: "张三", age: 18 }
AppStorage.setOrCreate<Person>("user", user)@Entry
@Component
struct Index {// @StorageProp("user")@StorageLink("user")userInfo: Person = { name: "李四", age: 28 } // 默认值build() {Column() {Text(`${this.userInfo.name}---${this.userInfo.age}`)Button("修改age").onClick(() => {this.userInfo.age++;})}.width("100%").height("100%")}
}

3.2. 组件共享

在这里插入图片描述
子组件中可共享源数据:

interface Person {name: string;age: number;
}const user: Person = { name: "张三", age: 18 }
AppStorage.setOrCreate<Person>("user", user)@Entry
@Component
struct Index {// @StorageProp("user")@StorageLink("user")userInfo: Person = { name: "李四", age: 28 } // 默认值build() {Column() {Text(`${this.userInfo.name}---${this.userInfo.age}`)Button("直接修改age").onClick(() => {this.userInfo.age++;})ChildA()}.width("100%").height("100%")}
}@Component
struct ChildA {@StorageProp("user") // 不会同步// @StorageLink("user") // 会同步user: Person = { name: "Tom", age: 20 }build() {Column({ space: 10 }) {Text(this.user.name + "----" + this.user.age).fontColor(Color.White)Button("ChildA修改age").onClick(() => {this.user.age++})}.width('100%').height(100).backgroundColor(Color.Blue)}
}

3.3. set() 及 get() 方法

  • 使用 AppStorage.set<ValueType>(key, value) 可以覆盖AppStorage中的数据,其中 key 是存储的键名,value 是要存储的新值。
  • 使用 AppStorage.get<ValueType>(key) 可以获取AppStorage中存储的数据,其中 ValueType 是数据的类型,key 是存储的键名。
    在这里插入图片描述
interface Person {name: string;age: number;
}const user: Person = { name: "张三", age: 18 }
AppStorage.setOrCreate<Person>("user", user)@Entry
@Component
struct Index {// @StorageProp("user")@StorageLink("user")userInfo: Person = { name: "李四", age: 28 } // 默认值build() {Column() {Text(`${this.userInfo.name}---${this.userInfo.age}`)Button("直接修改age").onClick(() => {this.userInfo.age++;})Button("通过set修改age").onClick(() => {AppStorage.set<Person>("user", { name: "李四", age: 28 })})Button("通过get获取user").onClick(() => {const user = AppStorage.get<Person>("user")console.log(`${user?.name}---${user?.age}`)})}.width("100%").height("100%")}
}

3.4. AppStorage.link(“user”);

  • 使用 const link: SubscribedAbstractProperty<ValueType> = AppStorage.Link(key) 可以创建一个与AppStorage中数据关联的链接,通过这个链接可以修改和获取数据。您可以使用 link.set(value) 来修改数据,使用 link.get() 来获取数据。这个链接允许您在不同组件之间共享数据,并保持数据的同步。

在这里插入图片描述

4. 总结

  • LocalStorage– 一个UIAbility状态(内存–非持久化–非全应用
  • AppStorage– 应用内状态–多UIAbility共享(内存–非持久化-退出App应用 数据消失
  • PersistenStroage–全局持久化状态(写入磁盘–持久化状态-退出App应用 数据不消失

http://www.ppmy.cn/news/1531040.html

相关文章

Halcon 分类

分类是将对象分配给一组类的单个实例的术语。由特定的特征来描述对象及类。例如&#xff0c;像素的颜色或图形的形状区域。为定义类&#xff0c;必须指定特征。通过基于已知对象的训练。训练后&#xff0c;分类器将对象的特征与可用的相关特征进行比较类并返回具有最大对应关系…

Creo百度云下载:附安装包+图文安装教程资源

Creo 11作为PTC公司推出的计算机辅助设计&#xff08;CAD&#xff09;软件的最新版本&#xff0c;在多个方面提供了显著的新功能和增强。今天与大家一起来看看Creo11新功能的详细归纳&#xff1a; 1. 核心建模与生产力增强 多体概念支持&#xff1a;Creo 11在核心建模环境方面…

Xcode16 iOS18 编译问题适配

问题1&#xff1a;ADClient编译报错问题 报错信息 Undefined symbols for architecture arm64:"_OBJC_CLASS_$_ADClient", referenced from:in ViewController.o ld: symbol(s) not found for architecture arm64 clang: error: linker command failed with exit co…

Dash稳定版更新

大家好&#xff0c;今天要和大家聊聊一个开发Python网页应用的超级神器——Dash 2.18.1稳定版本正式发布啦&#xff01;此次更新&#xff0c;针对2.18.0版本的问题进行了修复和优化&#xff0c;为我们带来了更为稳定、强大的开发体验。 Dash是什么&#xff1f; Dash是一款基于P…

【2024】前端学习笔记9-内部样式表-外部导入样式表-类选择器

学习笔记 内部样式表外部导入样式表类选择器&#xff1a;class 内部样式表 内部样式表是将 CSS 样式规则写在 HTML 文档内部。通过<style>标签在 HTML 文件的<head>部分定义样式。 简单示例&#xff1a; <!DOCTYPE html><html><head><style…

react hooks--useCallback

概述 useCallback缓存的是一个函数&#xff0c;主要用于性能优化!!! 基本用法 如何进行性能的优化呢&#xff1f; useCallback会返回一个函数的 memoized&#xff08;记忆的&#xff09; 值&#xff1b;在依赖不变的情况下&#xff0c;多次定义的时候&#xff0c;返回的值是…

暴雨讲堂:算力高速互联催化超节点开启AI新篇章

在2024中国国际北京通信展期间&#xff0c;暴雨副总裁孙辉介绍了暴雨信息在超节点方案的最新突破&#xff0c;彰显了暴雨信息在算力和网络技术上的强大创新能力。 随着人工智能技术的飞速发展&#xff0c;AI大模型的参数规模正以超越摩尔定律的速度急剧扩张。在此背景下&#…

嵌入式开发中学习C++的用处?

这个问题一直有同学在问&#xff0c;其实从我的角度是一定是需要学的&#xff0c;最直接的就是你面试大厂的嵌入式岗位或者相关岗位&#xff0c;最后一定会问c&#xff0c;而很多人是不会的&#xff0c;这就是最大的用处&#xff0c;至于从技术角度考量倒是其次&#xff0c;因为…