【鸿蒙开发】应用状态管理LocalStorage,AppStorage,PersistentStorage

news/2024/7/21 9:51:26 标签: harmonyos, 华为, 鸿蒙, typescript

 0. 应用状态管理

  • LocalStorage:页面级UI状态存储,通常用于UIAbility内、页面间的状态共享。
  • AppStorage:特殊的单例LocalStorage对象,由UI框架在应用程序启动时创建,为应用程序UI状态属性提供中央存储;
  • PersistentStorage:持久化存储UI状态,通常和AppStorage配合使用,选择AppStorage存储的数据写入磁盘,以确保这些属性在应用程序重新启动时的值与应用程序关闭时的值相同;
  • Environment:应用程序运行的设备的环境参数,环境参数会同步到AppStorage中,可以和AppStorage搭配使用。

1. LocalStorage 页面级UI状态存储

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

内存--非持久化--非全应用

1.1 概述

LocalStorage是ArkTS为构建页面级别状态变量提供存储的内存内“数据库”。

  • 应用程序可以创建多个LocalStorage实例,LocalStorage实例可以在页面内共享,也可以通过GetShared接口,实现跨页面、UIAbility实例内共享。
  • 组件树的根节点,即被@Entry装饰的@Component,可以被分配一个LocalStorage实例,此组件的所有子组件实例将自动获得对该LocalStorage实例的访问权限;
  • 被@Component装饰的组件最多可以访问一个LocalStorage实例和AppStorage,未被@Entry装饰的组件不可被独立分配LocalStorage实例,只能接受父组件通过@Entry传递来的LocalStorage实例。一个LocalStorage实例在组件树上可以被分配给多个组件。
  • LocalStorage中的所有属性都是可变的。

应用程序决定LocalStorage对象的生命周期。当应用释放最后一个指向LocalStorage的引用时,比如销毁最后一个自定义组件,LocalStorage将被JS Engine垃圾回收。

LocalStorage提供了两个装饰器:

  • @LocalStorageProp:单向同步关系。
  • @LocalStorageLink:双向同步关系。

1.2 页面内共享

  • 在页面创建LocalStorage实例,const storage = new LocalStorage({ key: value })
  • 把storage实例传入@Entry
  • 单向数据绑定:使用 @LocalStorageProp('user') 装饰器,可以在组件内对LocalStorage的值进行单向修改,仅在组件内生效。
  • 双向数据绑定:使用 @LocalStorageLink('user') 装饰器,可以实现全局范围内对LocalStorage的值进行双向修改,多个组件可以共享相同的LocalStorage实例。

1.3 示例

class Person {
  name?: string
  age?: number
}

const storage: LocalStorage = new LocalStorage({
  user: { name: "lili", age: 18 }
})

@Entry(storage)
@Component
struct Index {
  @LocalStorageProp("user")
  user: Person = { name: "Tom", age: 20 }

  build() {
    Column({ space: 10 }) {
      Text(this.user.name + "----" + this.user.age)
      Button("修改age").onClick(() => {
        this.user.age++
      })
      ChildA()
      ChildB()
    }
    .width('100%')
    .height('100%')
  }
}

@Component
struct ChildA {
  @LocalStorageProp("user")
  user: Person = { name: "Tom", age: 20 }

  build() {
    Column({ space: 10 }) {
      Text(this.user.name + "----" + this.user.age)
        .fontColor(Color.White)
      Button("修改age").onClick(() => {
        //不会同步
        this.user.age++
      })
    }
    .width('100%')
    .height(100)
    .backgroundColor(Color.Blue)
  }
}


@Component
struct ChildB {
  @LocalStorageLink("user")
  user: Person = { name: "Tom", age: 20 }

  build() {
    Column({ space: 10 }) {
      Text(this.user.name + "----" + this.user.age)
        .fontColor(Color.White)
      Button("修改age").onClick(() => {
        //会同步
        this.user.age++
      })
    }
    .width('100%')
    .height(100)
    .backgroundColor(Color.Brown)
  }
}

 

1.4 页面间共享

页面间共享LocalStorage实例,请确保在模拟器上进行测试以验证功能是否正常工作

  • 在UIAbility中创建LocalStorage,const storage = new LocalStorage({ key: value })
  • 把storage实例传入到 windowStage.loadContent 方法中
  • 在页面中获取LocalStorage实例, const storage = LocalStorage.GetShared() 
  • 把storage实例传入@Entry

1.5 示例

修改 EntryAbility.ts

import UIAbility from '@ohos.app.ability.UIAbility';
import hilog from '@ohos.hilog';
import window from '@ohos.window';

export default class EntryAbility extends UIAbility {
  onCreate(want, launchParam) {
    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate');
  }

  onDestroy() {
    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onDestroy');
  }

  storage: LocalStorage = new LocalStorage({
    user: {
      name: "lili", age: 18
    }
  })

  onWindowStageCreate(windowStage: window.WindowStage) {
    // Main window is created, set main page for this ability
    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate');

    windowStage.loadContent('pages/Index', this.storage, (err, data) => {
      if (err.code) {
        hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
        return;
      }
      hilog.info(0x0000, 'testTag', 'Succeeded in loading the content. Data: %{public}s', JSON.stringify(data) ?? '');
    });
  }

  onWindowStageDestroy() {
    // Main window is destroyed, release UI related resources
    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageDestroy');
  }

  onForeground() {
    // Ability has brought to foreground
    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onForeground');
  }

  onBackground() {
    // Ability has back to background
    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onBackground');
  }
}

新建 models/index.ets

export class Person {
  name?: string
  age?: number
}

修改 pages/Index.ets

import router from '@ohos.router'
import { Person } from '../models'

const storage = LocalStorage.GetShared()


@Entry(storage)
@Component
struct Index {
  @LocalStorageLink("user")
  user: Person = { name: "Tom", age: 28 }

  build() {
    Column() {
      Text(`${this.user.name}--${this.user.age}`)
      Button("修改age").onClick(() => {
        this.user.age++
      })

      Button("去otherPage").onClick(() => {
        router.pushUrl({
          url: "pages/OtherPage"
        })
      })

    }
    .width('100%')
    .height('100%')
  }
}

 新建页面 OtherPage.ets

import router from '@ohos.router'
import { Person } from '../models'

const storage = LocalStorage.GetShared()

@Entry(storage)
@Component
struct OtherPage {
  @LocalStorageLink("user")
  user: Person = { name: "lisa", age: 38 }

  build() {
    Row() {
      Column() {
        Text(`${this.user.name}--${this.user.age}`)
        Button("修改age").onClick(() => {
          this.user.age++
        })
        Button("返回").onClick(() => {
          router.back()
        })
      }
      .width('100%')
    }
    .height('100%')
  }
}

2. AppStorage 应用全局的UI状态存储

AppStorage是应用全局的UI状态存储,是和应用的进程绑定的,由UI框架在应用程序启动时创建,为应用程序UI状态属性提供中央存储。

内存--非持久化--应用退出再次启动后 数据消失

2.1 概述

AppStorage是在应用启动的时候会被创建的单例。它的目的是为了提供应用状态数据的中心存储,这些状态数据在应用级别都是可访问的。AppStorage将在应用运行过程保留其属性。属性通过唯一的键字符串值访问。

AppStorage可以和UI组件同步,且可以在应用业务逻辑中被访问。

AppStorage中的属性可以被双向同步,数据可以是存在于本地或远程设备上,并具有不同的功能,比如数据持久化(详见PersistentStorage)。这些数据是通过业务逻辑中实现,与UI解耦,如果希望这些数据在UI中使用,需要用到@StorageProp和@StorageLink。

2.2 从UI内部使用AppStorage

  • 使用 AppStorage.SetOrCreate(key, value) 初始化AppStorage中的数据。
  • 使用 @StorageProp('user') 装饰器,可以在组件内对AppStorage的值进行单向数据绑定。
  • 使用 @StorageLink('user') 装饰器,可以实现全局范围内对AppStorage的值进行双向数据绑定,多个组件可以共享相同的AppStorage实例。

修改 pages/Index.ets

import router from '@ohos.router'
import { Person } from '../models'

AppStorage.SetOrCreate<Person>("user", { name: "lili", age: 18 })

@Entry
@Component
struct Index {
  @StorageLink("user")
  user: Person = { name: "Tom", age: 20 }

  build() {
    Column({ space: 10 }) {
      Text(this.user.name + "----" + this.user.age)
      Button("修改age").onClick(() => {
        this.user.age++
      })
      Button("跳转到TwoPage").onClick(() => {
        router.pushUrl({
          url: "pages/TwoPage"
        })
      })
    }
    .width('100%')
    .height('100%')
  }
}

添加页面 TwoPage.ets

import router from '@ohos.router'
import { Person } from '../models'

@Entry
@Component
struct TwoPage {
  @StorageLink("user")
  user: Person = { name: "Tom", age: 20 }

  build() {
    Column({ space: 10 }) {
      Text(this.user.name + "----" + this.user.age)
      Button("修改age").onClick(() => {
        this.user.age++
      })
      Button("返回Index页面").onClick(() => {
        router.back()
      })

    }.width('100%')
    .height('100%')
  }
}

 

2.3 从应用逻辑使用AppStorage

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

修改 pages/Index.ets 

import promptAction from '@ohos.promptAction'
import { Person } from '../models'

AppStorage.SetOrCreate<Person>("user", { name: "lili", age: 18 })

@Entry
@Component
struct Index {
  @StorageLink("user")
  user: Person = { name: "Tom", age: 20 }

  build() {
    Column({ space: 10 }) {
      Text(this.user.name + "----" + this.user.age)
      Button("修改age").onClick(() => {
        this.user.age++
      })

      Button("获取user信息").onClick(() => {
        const user = AppStorage.Get<Person>("user");
        console.info(user.name)
        promptAction.showToast({
          message: "user.name:" + user.name + " user.age:" + user.age,
          bottom: 20
        })
      })
      Button("覆盖user信息").onClick(() => {
        AppStorage.Set<Person>("user", { name: "Tom", age: 28 });
      })

      Button("通过 Link 覆盖user信息").onClick(() => {
        const userLink: SubscribedAbstractProperty<Person> = AppStorage.Link("user");
        userLink.set({ name: "Jack", age: 38 })
      })

    }
    .width('100%')
    .height('100%')
  }
}

 

3. PersistentStorage 持久化存储UI状态

写入磁盘--持久化状态--应用退出 数据不消失

3.1 概述

PersistentStorage将选定的AppStorage属性保留在设备磁盘上。应用程序通过API,以决定哪些AppStorage属性应借助PersistentStorage持久化。UI和业务逻辑不直接访问PersistentStorage中的属性,所有属性访问都是对AppStorage的访问,AppStorage中的更改会自动同步到PersistentStorage。

3.2 限制条件

PersistentStorage允许的类型和值有:

  • number, string, boolean, enum 等简单类型。
  • 可以被JSON.stringify()和JSON.parse()重构的对象。例如Date, Map, Set等内置类型则不支持,以及对象的属性方法不支持持久化。

PersistentStorage不允许的类型和值有:

  • 不支持嵌套对象(对象数组,对象的属性是对象等)。因为目前框架无法检测AppStorage中嵌套对象(包括数组)值的变化,所以无法写回到PersistentStorage中。
  • 不支持undefined 和 null 。

PersistentStorage的持久化变量最好是小于2kb的数据。

PersistentStorage只能在UI页面内使用,否则将无法持久化数据。

3.3 接口

PersistProp

static PersistProp<T>(key: string, defaultValue: T): void

将AppStorage中key对应的属性持久化到文件中。该接口的调用通常在访问AppStorage之前。

DeleteProp

static DeleteProp(key: string): void

将key对应的属性从PersistentStorage删除,后续AppStorage的操作,对PersistentStorage不会再有影响。

PersistProps

static PersistProps(properties: {key: string, defaultValue: any;}[]): void

行为和PersistProp类似,不同在于可以一次性持久化多个数据,适合在应用启动的时候初始化。

Keys

static Keys(): Array<string>

返回所有持久化属性的key的数组。

3.4 简单数据类型的持久化

PersistentStorage.PersistProp("num", 100)

@Entry
@Component
struct Index {
  @StorageLink("num")
  num: number = 0

  build() {
    Column({ space: 10 }) {
      Text(`${this.num}`)
      Button("修改age").onClick(() => {
        this.num++
      })
    }
    .width('100%')
    .height('100%')
  }
}

 

3.5 复杂数据类型的持久化

修改 pages/Index.ets

import router from '@ohos.router'
import { Person } from '../models'


PersistentStorage.PersistProp("user", '{"name":"lili","age":18}')

@Entry
@Component
struct Index {
  @StorageLink("user")
  @Watch("update")
  userData: string = ""
  @State user: Person = JSON.parse(this.userData)

  update() {
    this.user = JSON.parse(this.userData)
  }

  build() {
    Column() {
      Text(this.user.name + "----" + this.user.age)
      Button("修改age").onClick(() => {
        this.user.age++
        AppStorage.Set<string>("user", JSON.stringify(this.user))
      })
      Button("去其他页面").onClick(() => {
        router.pushUrl({
          url: "pages/OtherPage"
        })
      })

      Button("获取").onClick(() => {
        const user = AppStorage.Get<string>("user")
        console.log(user)
      })

      Button("AppStorage Set 修改1").onClick(() => {
        AppStorage.Set<string>("user", JSON.stringify({ "name": "lisa", "age": 20 }))
      })

      Button("AppStorage Link 修改2").onClick(() => {
        const userLink: SubscribedAbstractProperty<string> = AppStorage.Link("user")
        userLink.set(JSON.stringify({ "name": "jack", "age": 20 }))
      })
    }
    .width("100%")
    .height("100%")
  }
}


添加页面 OtherPage.ets

import router from '@ohos.router'
import { Person } from '../models'


@Entry
@Component
struct OtherPage {
  @StorageLink("user")
  userData: string = ""
  @State user: Person = JSON.parse(this.userData)

  build() {
    Column() {
      Text(this.user.name + "----" + this.user.age)
      Button("修改age").onClick(() => {
        this.user.age++
        AppStorage.Set<string>("user", JSON.stringify(this.user))
      })
      Button("返回Index页面").onClick(() => {
        router.back()
      })
    }
    .width("100%")
    .height("100%")
  }
}


http://www.niftyadmin.cn/n/5481497.html

相关文章

Unity数据持久化—Json存档

项目需求为&#xff1a; 1.实现存档列表&#xff0c;显示存档截图&#xff0c;可以查看之前保存的所有存档 2.点击存档直接加载到场景 首先&#xff0c;定义两个类&#xff0c;用于声明存档列表和存档所需要的List [System.Serializable] public class SaveData {//存储目标…

【Linux的进程篇章 - 环境变量的理解】

Linux学习笔记---007 Linux之进程优先级、环境变量以及地址空间的理解1、进程优先级1.1、什么是优先级&#xff1f;1.2、为什么要有优先级&#xff1f;1.3、Linux的优先级特点以及查看方式1.4、进程的几个特性 2、环境变量2.1、概念2.2、命令行参数2.2.1、什么是命令行参数&…

vue 配置 postcss-px2rem

postcss-px2rem postcss-px2rem 就是为了让我们直接在将代码中 px 自动转化成对应的 rem 的一个插件 (rem 是 root em 的缩写&#xff0c;rem 不是相对于当前元素&#xff0c;而是相对于根元素&#xff0c;所以&#xff0c;不论什么位置&#xff0c;使用 rem 单位都是相对于根元…

掌握Nginx缓存策略:提高网站性能,降低响应时间

欢迎来到我的博客&#xff0c;代码的世界里&#xff0c;每一行都是一个故事 掌握Nginx缓存策略&#xff1a;提高网站性能&#xff0c;降低响应时间 前言缓存的原理Nginx缓存的工作原理 Nginx缓存模块1. proxy_cache 模块2. fastcgi_cache 模块 缓存位置1. 内存缓存2. 磁盘缓存总…

中老年人高血糖预防需知:少碰两黄一白,四指标严格控制!

对于血糖不好的人来说&#xff0c;尤其是中老年人&#xff0c;饮食上的调整非常重要。 “少碰两黄一白”是一个很好的饮食原则&#xff0c;可以帮助稳定血糖&#xff0c;预防糖尿病及其并发症的发生。 “两黄”指的是油炸食物和含糖量高的食物&#xff0c;长期摄入会导致身体肥…

关于Linux下的进程等待(进程篇)

目录 为什么存在进程等待&#xff1f;进程等待是在做什么&#xff1f; 怎样去执行进程等待&#xff1f; status options 为什么存在进程等待&#xff1f;进程等待是在做什么&#xff1f; 代码示例&#xff1a;模仿僵尸进程 #include <stdio.h> #include <unistd.…

如何修改 MySQL 8.0 的密码

在 MySQL 8.0 中修改用户密码是数据库管理的一项基本任务。本教程将引导您完成这一过程&#xff0c;确保即使是初学者也能理解并成功执行。 介绍 MySQL 是最流行的关系数据库管理系统之一。作为数据库管理员或开发人员&#xff0c;您可能需要更改用户的密码来保证账户安全。本…

极狐GitLab对接OAuth2实现SSO

本文作者&#xff1a;极狐(GitLab) 高级解决方案架构师 武让 GitLab 是一个全球知名的一体化 DevOps 平台&#xff0c;很多人都通过私有化部署 GitLab 来进行源代码托管。极狐GitLab 是 GitLab 在中国的发行版&#xff0c;专门为中国程序员服务。可以一键式部署极狐GitLab。 企…