HarmonyOS 应用级状态管理(LocalStorage、AppStorage、PersistentStorage)

HarmonyOS 应用级状态管理

1. LocalStorage:页面级UI状态存储

1.1 概念

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

1.2 应用逻辑使用LocalStorage

ts 复制代码
/**
 * 和 js 用法几乎一致
 * 初始化的时候需要传入一个对象使用
 * 修改的时候通过key 修改 value的值
 */
const storage = new LocalStorage({name: "李四"})

storage.set('name', '张三')

console.log(storage.get("name"))



@Entry
@Component
struct LearnStorage {
  @State message: string = 'Hello World'

  build() {
    Row() {
      Column() {
        Text(this.message)
          .fontSize(50)
          .fontWeight(FontWeight.Bold)
      }
      .width('100%')
    }
    .height('100%')
  }
}

1.3 从UI内部使用LocalStorage(双向同步)

  • 除了应用程序逻辑使用LocalStorage,还可以借助LocalStorage相关的两个装饰器@LocalStorageProp和@LocalStorageLink,在UI组件内部获取到LocalStorage实例中存储的状态变量。
1.3.1 例子
  • 使用构造函数创建LocalStorage实例storage;
  • 使用@Entry装饰器将storage添加到LearnStorage顶层组件中;
  • @LocalStorageLink绑定LocalStorage对给定的属性,建立双向数据同步
ts 复制代码
@Component
struct Child {
  // 子组件可以直接使用
  @LocalStorageLink('name') storageName: string = '张三';
  build() {
    Column() {
      Text('子组件---'+this.storageName)
    }
  }
}




/**
 * 和 js 用法几乎一致
 * 初始化的时候需要传入一个对象使用
 * 修改的时候通过key 修改 value的值
 */
const storage = new LocalStorage({})



@Entry(storage)
@Component
struct LearnStorage {

  /**
   * @LocalStorageLink变量装饰器与LocalStorage中的'name'属性建立双向绑定
   * 当@LocalStorageLink(key)装饰的数值改变被观察到时,修改将被同步回LocalStorage对应属性键值key的属性中。
   LocalStorage中属性键值key对应的数据一旦改变,属性键值key绑定的所有的数据(包括双向@LocalStorageLink和单向@LocalStorageProp)都将同步修改;
   当@LocalStorageLink(key)装饰的数据本身是状态变量,它的改变不仅仅会同步回LocalStorage中,还会引起所属的自定义组件的重新渲染

      必须指定默认值,如果localstorage对应的key里面有值优先取localstorage里面的值,没有则使用默认值
   */
  @LocalStorageLink('name') storageName: string = '张三';

  build() {
    Row() {
      Column() {
        Text(this.storageName)
          .fontSize(50)
          .fontWeight(FontWeight.Bold)
        Button('看看Storage').onClick(() => {
          this.storageName = '王五'
          // 也会打印王五
          console.log(storage.get('name'))
        })
        Child()
      }
      .width('100%')
    }
    .height('100%')
  }
}

1.4 从UI内部使用LocalStorage(单向同步)

ts 复制代码
@Component
struct Child {
  // 子组件可以直接使用
  @LocalStorageProp('name') storageName: string = '张三';
  build() {
    Column() {
      Text('子组件---'+this.storageName)
    }
  }
}




/**
 * 和 js 用法几乎一致
 * 初始化的时候需要传入一个对象使用
 * 修改的时候通过key 修改 value的值
 */
const storage = new LocalStorage({})



@Entry(storage)
@Component
struct LearnStorage {

  /**

   必须指定默认值,如果localstorage对应的key里面有值优先取localstorage里面的值,没有则使用默认值
   */
  @LocalStorageProp('name') storageName: string = '张三';

  build() {
    Row() {
      Column() {
        Text(this.storageName)
          .fontSize(50)
          .fontWeight(FontWeight.Bold)
        Button('看看Storage').onClick(() => {
          this.storageName = '王五'
          // 单项同步不会修改storage里的数据
          console.log(storage.get('name'))
        })
        Child()
      }
      .width('100%')
    }
    .height('100%')
  }
}

1.5 将LocalStorage实例从UIAbility共享到一个或多个视图(全局共享)

  • 上面的实例中,LocalStorage的实例仅仅在一个@Entry装饰的组件和其所属的子组件(一个页面)中共享,如果希望其在多个视图中共享,可以在所属UIAbility中创建LocalStorage实例,并调用windowStage.loadContent。
1.5.1 EntryAbility
ts 复制代码
import UIAbility from '@ohos.app.ability.UIAbility';
import hilog from '@ohos.hilog';
import window from '@ohos.window';

// 创建storage
const localStorage: LocalStorage = new LocalStorage({globalName: "全局的名字"});

export default class EntryAbility extends UIAbility {

  // 挂载到实例里
  storage: LocalStorage = localStorage


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

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

  async onWindowStageCreate(windowStage: window.WindowStage) {
    windowStage.loadContent('pages/LearnStorage', this.storage);
  }

  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');
  }
}
1.5.2 页面中使用
ts 复制代码
// 通过GetShared接口获取stage共享的LocalStorage实例
const storage = LocalStorage.GetShared()

console.log(JSON.stringify(storage))
// 注入
@Entry(storage)
@Component
struct LearnStorage {

  /**
      注意:要在模拟器中运行
    使用prop单项绑定 或者 link双向绑定
   */
  @LocalStorageLink('globalName') globalName: string = '李四';
  // globalName = 'aa'

  build() {
    Row() {
      Column() {
        Text(this.globalName)
          .fontSize(50)
          .fontWeight(FontWeight.Bold)
        Button('看看Storage').onClick(() => {
          this.globalName = '王五'
          // 单项同步不会修改storage里的数据
          console.log(storage.get('name'))
        })
      }
      .width('100%')
    }
    .height('100%')
  }
}

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

2.1 概念

  • AppStorage是应用全局的UI状态存储,是和应用的进程绑定的,由UI框架在应用程序启动时创建,为应用程序UI状态属性提供中央存储。
  • 和AppStorage不同的是,LocalStorage是页面级的,通常应用于页面内的数据共享。而AppStorage是应用级的全局状态共享,还相当于整个应用的"中枢",持久化数据PersistentStorage和环境变量Environment都是通过和AppStorage中转,才可以和UI交互。
  • AppStorage是在应用启动的时候会被创建的单例。它的目的是为了提供应用状态数据的中心存储,这些状态数据在应用级别都是可访问的。AppStorage将在应用运行过程保留其属性。属性通过唯一的键字符串值访问。
  • AppStorage可以和UI组件同步,且可以在应用业务逻辑中被访问。
  • AppStorage中的属性可以被双向同步,数据可以是存在于本地或远程设备上,并具有不同的功能,比如数据持久化(详见PersistentStorage)。这些数据是通过业务逻辑中实现,与UI解耦,如果希望这些数据在UI中使用,需要用到@StorageProp和@StorageLink

2.2 从应用逻辑使用AppStorage

  • AppStorage是单例,它的所有API都是静态的
ts 复制代码
// 有则修改没有则创建
AppStorage.SetOrCreate('name', 'app的名字');


console.log(AppStorage.Get('name'))


@Entry()
@Component
struct LearnStorage {

  build() {
    Row() {
      Column() {
        Text('哈哈哈')
      }
      .width('100%')
    }
    .height('100%')
  }
}

2.3 从UI内部使用AppStorage

  • @StorageLink变量装饰器与AppStorage配合使用,正如@LocalStorageLink与LocalStorage配合使用一样。此装饰器使用AppStorage中的属性创建双向数据同步
ts 复制代码
// 有则修改没有则创建
AppStorage.SetOrCreate('name', 'app的名字');


console.log(AppStorage.Get('name'))


@Entry()
@Component
struct LearnStorage {
  /**
   * 对AppStorage 里面的数据进行双向绑定
   * 同样使用prop为单向数据流
   */
  @StorageLink('name') name: string = '李四'

  build() {
    Row() {
      Column() {
        Text(this.name)
        Button('查看数据')
          .onClick(() => {
            this.name = '王五'
            // 数据也会改变
            console.log(AppStorage.Get('name'))
          })
      }
      .width('100%')
    }
    .height('100%')
  }
}

3.PersistentStorage:持久化存储UI状态

3.1 概念

  • PersistentStorage将选定的AppStorage属性保留在设备磁盘上。应用程序通过API,以决定哪些AppStorage属性应借助PersistentStorage持久化。UI和业务逻辑不直接访问PersistentStorage中的属性,所有属性访问都是对AppStorage的访问,AppStorage中的更改会自动同步到PersistentStorage。
  • PersistentStorage和AppStorage中的属性建立双向同步。应用开发通常通过AppStorage访问PersistentStorage,另外还有一些接口可以用于管理持久化属性,但是业务逻辑始终是通过AppStorage获取和设置属性的。

3.2 注意

3.2.1 允许的类型和值
    1. 使用简单类型如 number, string, boolean, enum。
    1. 可以使用 JSON.stringify() 和 JSON.parse() 的对象,但不支持内置类型如 Date, Map, Set。
3.2.2 不允许的类型和值
    1. 避免嵌套对象,包括对象数组和对象属性是对象。
    1. 不支持 undefined 和 null。
3.2.3 最佳实践和限制
    1. 避免持久化大型和经常变化的数据。
    1. 持久化变量最好小于 2kb。
    1. 不要过度持久化数据,可能影响 UI 渲染性能。
    1. 对于大量数据存储需求,建议使用数据库 API。
    1. 仅在 UI 页面内使用 PersistentStorage,否则无法持久化数据

3.3 使用

ts 复制代码
// 存入的数据会一直存在
PersistentStorage.PersistProp('token', '后端获取的token');
// 可以使用AppStorage 进行获取
console.log(AppStorage.Get('token'))

// 这样定义的不会持久化,应用被杀死 数据会丢失
// AppStorage.SetOrCreate('token', '小token')

@Entry()
@Component
struct LearnStorage {
  /**
   * 双向绑定
   * 也可以是会用StorageLink进行获取
   */
  @StorageLink('token') token: string = ''


  build() {
    Row() {
      Column() {
        Text(this.token)
        Button('修改数据')
          .onClick(() => {
            this.token = '修改的token,并且持久化'
          })
      }
      .width('100%')
    }
    .height('100%')
  }
}
相关推荐
长弓三石40 分钟前
鸿蒙网络编程系列44-仓颉版HttpRequest上传文件示例
前端·网络·华为·harmonyos·鸿蒙
SameX3 小时前
鸿蒙 Next 电商应用安全支付与密码保护实践
前端·harmonyos
SuperHeroWu73 小时前
【HarmonyOS】键盘遮挡输入框UI布局处理
华为·harmonyos·压缩·keyboard·键盘遮挡·抬起
sanzk8 小时前
华为鸿蒙应用开发
华为·harmonyos
SoraLuna12 小时前
「Mac畅玩鸿蒙与硬件28」UI互动应用篇5 - 滑动选择器实现
macos·ui·harmonyos
ClkLog-开源埋点用户分析13 小时前
ClkLog企业版(CDP)预售开启,更有鸿蒙SDK前来助力
华为·开源·开源软件·harmonyos
mg66813 小时前
鸿蒙系统的优势 开发 环境搭建 开发小示例
华为·harmonyos
模拟IC攻城狮14 小时前
华为海思招聘-芯片与器件设计工程师-模拟芯片方向- 机试题-真题套题题目——共8套(每套四十题)
嵌入式硬件·华为·硬件架构·芯片
lqj_本人14 小时前
鸿蒙next选择 Flutter 开发跨平台应用的原因
flutter·华为·harmonyos
lqj_本人14 小时前
使用 Flutter 绘制一个棋盘
harmonyos