目录
LocalStorage 页面级UI状态存储
LocalStorage是ArkTS为构建页面级别状态变量提供存储的内存内的“数据库”。
应用程序可以创建多个LocalStorage实例,LocalStorage实例可以在页面内共享,也可以通过getSharedLocalStorage接口,实现跨页面、跨UIAbility实例共享。
组件树的根节点,即被@Entry装饰的@Component,可以被分配一个LocalStorage实例,此组件的所有子组件实例将自动获得对该LocalStorage实例的访问权限。
一、应用逻辑使用LocalStorage
// 创建新实例并使用给定对象初始化
let para: Record<string,number> = { 'PropA': 47 };
let storage: LocalStorage = new LocalStorage(para);
// 获取值,propA只是一个普通变量,没有绑定
let propA: number | undefined = storage.get('PropA') // propA == 47
// link1和link2为双向绑定的变量
let link1: SubscribedAbstractProperty<number> = storage.link('PropA'); // link1.get() == 47
let link2: SubscribedAbstractProperty<number> = storage.link('PropA'); // link2.get() == 47
// prop为单向绑定的变量
let prop: SubscribedAbstractProperty<number> = storage.prop('PropA'); // prop.get() == 47
//修改后产生的效果
link1.set(48); // two-way sync: link1.get() == link2.get() == prop.get() == 48
prop.set(1); // one-way sync: prop.get() == 1; but link1.get() == link2.get() == 48
link1.set(49); // two-way sync: link1.get() == link2.get() == prop.get() == 49
二、从UI内部使用LocalStorage
// 创建新实例并使用给定对象初始化
let para: Record<string, number> = { 'PropA': 47 };
let storage: LocalStorage = new LocalStorage(para);
// 绑定到页面的根节点
// 使LocalStorage可从@Component组件访问
@Entry(storage)
@Component
struct CompA {
// @LocalStorageLink变量装饰器与LocalStorage中的'PropA'属性建立双向绑定
@LocalStorageLink('PropA') parentLinkNumber: number = 1; // 1 是默认值,当LocalStorage中没有PropA时使用
build () {
Column () {
// @Component子组件自动获得对CompA LocalStorage实例的访问权限。
child()
}
}
}
@Component
struct Child {
// @LocalStorageLink变量装饰器与LocalStorage中的'PropA'属性建立双向绑定
@LocalStorageLink('PropA') childLinkNumber: number = 1;
build () {
...
}
}
@LocalStorageProp的用法与 @LocalStorageLink 一样,只不过是单向绑定的
三、将LocalStorage实例从UIAbility共享到一个或多个视图
如果希望其在多个视图中共享,可以在所属UIAbility中创建LocalStorage实例,并调用windowStage.loadContent
export default class EntryAbility extends UIAbility {
storage: LocalStorage = new LocalStorage({ 'PropA': 47 });
onWindowStageCreate(windowStage: window.WindowStage) {
windowStage.loadContent('pages/Index', this.storage);
}
}
可以在多个视图中共享
// index.ets
// 通过getShared接口获取stage共享的LocalStorage实例
let storage = LocalStorage.getShared()
@Entry(storage)
@Component
struct Index {
@LocalStorageLink('PropA') propA: number = 1;
build () {
Column () {
Text(`${this.propA}`)
}
}
}
// page.ets
// 通过getShared接口获取stage共享的LocalStorage实例
let storage = LocalStorage.getShared()
@Entry(storage)
@Component
struct Page {
@LocalStorageLink('PropA') propA: number = 1;
build () {
Row () {
Text(`${this.propA}`)
}
}
}
对于开发者更建议使用这个方式来构建LocalStorage的实例,并且在创建LocalStorage实例的时候就写入默认值,因为默认值可以作为运行异常的备份,也可以用作页面的单元测试。
四、其它用法
1、自定义组件接收LocalStorage实例
除了根节点可通过@Entry来接收LocalStorage实例,自定义组件(子节点)也可以通过构造参数来传递LocalStorage实例。
2、Navigation组件和LocalStorage联合使用
AppStorage 应用级UI状态存储
AppStorage是在应用启动时创建的单例,用于提供应用状态数据的中心存储。这些状态数据在应用级别可访问。AppStorage在应用运行过程中保留其属性。
一、从应用逻辑使用AppStorage
AppStorage是单例,它的所有API都是静态的,使用方法类似于LocalStorage中对应的非静态方法。
AppStorage.setOrCreate('PropA', 47);
// 普通变量
let propA: number | undefined = AppStorage.get('PropA') // propA in AppStorage == 47
// 双向绑定状态变量
let link1: SubscribedAbstractProperty<number> = AppStorage.link('PropA'); // link1.get() == 47
let link2: SubscribedAbstractProperty<number> = AppStorage.link('PropA'); // link2.get() == 47
// 单向绑定状态变量
let prop: SubscribedAbstractProperty<number> = AppStorage.prop('PropA'); // prop.get() == 47
// 修改值
link1.set(48); // two-way sync: link1.get() == link2.get() == prop.get() == 48
prop.set(1); // one-way sync: prop.get() == 1; but link1.get() == link2.get() == 48
link1.set(49); // two-way sync: link1.get() == link2.get() == prop.get() == 49
// 获取值
AppStorage.get<number>('PropA') // == 49
link1.get() // == 49
link2.get() // == 49
prop.get() // == 49
二、从UI内部使用AppStorage
AppStorage.setOrCreate('PropA', 47);
@Entry()
@Component
struct CompA {
@StorageLink('PropA') storageLink: number = 1;
build() {
Column() {
Text(`From AppStorage ${this.storageLink}`)
}
}
}
PersistentStorage:持久化存储UI状态
前面介绍的LocalStorage和AppStorage都是运行时的内存
但是在应用退出再次启动后,依然能保存选定的结果,是应用开发中十分常见的现象,这就需要用到PersistentStorage。
PersistentStorage和UI实例相关联,持久化操作需要在UI实例初始化成功后(即loadContent传入的回调被调用时)才可以被调用,早于该时机调用会导致持久化失败。
// EntryAbility.ets
onWindowStageCreate(windowStage: window.WindowStage): void {
windowStage.loadContent('pages/Index', (err) => {
if (err.code) { return; }
PersistentStorage.persistProp('aProp', 47);
});
}
新应用安装后首次启动运行:
a.调用persistProp初始化PersistentStorage,首先查询在PersistentStorage本地文件中是否存在“aProp”,查询结果为不存在,因为应用是第一次安装。
b.接着查询属性“aProp”在AppStorage中是否存在,依旧不存在。
c.在AppStorge中创建名为“aProp”的number类型属性,属性初始值是定义的默认值47。
d.PersistentStorage将属性“aProp”和值47写入磁盘,AppStorage中“aProp”对应的值和其后续的更改将被持久化。
e.在Index组件中创建状态变量@StorageLink(‘aProp’) aProp,和AppStorage中“aProp”双向绑定,在创建的过程中会在AppStorage中查找,成功找到“aProp”,所以使用其在AppStorage找到的值47。
触发点击事件后:
a.状态变量@StorageLink(‘aProp’) aProp改变,触发Text组件重新刷新。
b.@StorageLink装饰的变量是和AppStorage中建立双向同步的,所以@StorageLink(‘aProp’) aProp的变化会被同步回AppStorage中。
c.AppStorage中“aProp”属性的改变会同步到所有绑定该“aProp”的单向或者双向变量,在本示例中没有其他的绑定“aProp”的变量。
d.因为“aProp”对应的属性已经被持久化,所以在AppStorage中“aProp”的改变会触发PersistentStorage,将新的改变写入本地磁盘。
后续启动应用:
a.执行PersistentStorage.persistProp(‘aProp’, 47),在首先查询在PersistentStorage本地文件查询“aProp”属性,成功查询到。
b.将在PersistentStorage查询到的值写入AppStorage中。
c.在Index组件里,@StorageLink绑定的“aProp”为PersistentStorage写入AppStorage中的值,即为上一次退出应用存入的值。
总结
PersistentStorage.persistProp(‘aProp’, 47); 告知将属性aProp持久化,后续aProp属性值的修改和获取都通过AppStorge的使用方式来进行
在AppStorage获取对应属性:
AppStorage.get<number>('aProp'); // returns 47
或在组件内部定义:
@StorageLink('aProp') aProp: number = 48;
Environment:设备环境查询
一、介绍
开发者如果需要应用程序运行的设备的环境参数,以此来作出不同的场景判断,比如多语言,暗黑模式等,需要用到Environment设备环境查询。
设备环境到Component的更新链:Environment –> AppStorage –>Component。
@StorageProp关联的环境参数可以在本地更改,但不能同步回AppStorage中,因为应用对环境变量参数是不可写的,只能在Environment中查询。
二、使用
Environment和UIContext相关联,需要在UIContext明确的时候才可以调用。
可以通过在runScopedTask里明确上下文。如果没有在UIContext明确的地方调用,将导致无法查询到设备环境数据。
// EntryAbility.ets
import { UIAbility } from '@kit.AbilityKit';
import { window } from '@kit.ArkUI';
export default class EntryAbility extends UIAbility {
onWindowStageCreate(windowStage: window.WindowStage) {
windowStage.loadContent('pages/Index');
let window = windowStage.getMainWindow()
window.then(window => {
let uicontext = window.getUIContext()
uicontext.runScopedTask(() => {
// 使用Environment.envProp将设备运行的环境变量存入AppStorage中:
Environment.envProp('languageCode', 'en');
})
})
}
}
应用逻辑使用Environment
// 使用Environment.EnvProp将设备运行languageCode存入AppStorage中;
Environment.envProp('languageCode', 'en');
// 从AppStorage获取单向绑定的languageCode的变量
const lang: SubscribedAbstractProperty<string> = AppStorage.prop('languageCode');
if (lang.get() === 'zh') {
console.info('你好');
} else {
console.info('Hello!');
}
从UI中访问Environment参数
@Entry
@Component
struct Index {
@StorageProp('languageCode') languageCode: string = 'en';
build() {
Row() {
Column() {
// 输出当前设备的languageCode
Text(this.languageCode)
}
}
}
}
行者常至,为者常成!