OpenHarmony 入门——ArkUI 跨页面数据同步和页面级UI状态存储LocalStorage小结(二)

文章大纲

引言

前面一篇文章主要介绍了OpenHarmony 入门------ArkUI 管理跨页面和应用级LocalStorage 页面级UI状态存储小结(一)的相关语法和基础理论知识,这篇就好好学习下其用法。

一、在代码逻辑使用LocalStorage

在代码逻辑使用LocalStorage意思是通过相关的API 来建立联系并实现同步。

javascript 复制代码
let para: Record<string,number> = { 'PropA': 47 };
let storage: LocalStorage = new LocalStorage(para); // 创建新实例并使用给定对象初始化
let propA: number | undefined = storage.get('PropA') // propA == 47
let link1: SubscribedAbstractProperty<number> = storage.link('PropA'); // link1.get() == 47
let link2: SubscribedAbstractProperty<number> = storage.link('PropA'); // link2.get() == 47
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

除了应用程序逻辑使用LocalStorage,还可以借助LocalStorage相关的两个装饰器@LocalStorageProp和@LocalStorageLink,在UI组件内部获取到LocalStorage实例中存储的状态变量

以@LocalStorageLink为例:

  • 使用构造函数创建LocalStorage实例storage;
  • 使用@Entry装饰器将storage添加到LocalParent顶层组件中;
  • @LocalStorageLink绑定LocalStorage对给定的属性,建立双向数据同步
javascript 复制代码
//1、LocalStorage
let obj :Record<string, number> = {'PropA': 66}
// 创建新实例并使用给定对象初始化,LocalStorage接收的是一个Object
let storageObj = new LocalStorage(obj);

@Component
struct LocalChild {
  @LocalStorageLink('PropA') propALinked : number = 88;

  build() {
    Column() {
      // 初始化时读取LocalStorage变量的值66,在按钮点击一次后依然为66不变
      Text(`LocalStorage: ${storageObj.get('PropA')}`).width('100%').height(36)
      //初始化为storage的值,再点击了自己的按钮一次后为
      Text(`LocalStorageLink: ${this.propALinked}`).width('100%').height(36)
      Button(`在LocalChild中读取自己的@LocalStorageLink: ${this.propALinked}`)
        .onClick(() => {
            // 更新@LocalStorageLink变量的值,Text上的值会改变
          this.propALinked += 2;
        }).width('100%').height('36')
    }
  }
}

// ???使LocalStorage可从@Component组件访问,好像我传不传过来都不影响结果呀????
@Entry(storageObj)
@Component
struct LocalParent {
  // @LocalStorageLink变量装饰器与LocalStorage中的'PropA'属性建立双向绑定
  @LocalStorageLink('PropA') storLinkP: number = 68;
  build() {
    Column({ space: 15 }) {
      Text(`LocalParent LocalStorage: ${storageObj.get('PropA')}`).width('100%').height(36)
      Button(`LocalParent 中LocalStorageLink: ${this.storLinkP}`).width('100%') // initial value from LocalStorage will be 66, because 'PropA' initialized already
        .onClick(() => this.storLinkP += 3)
      // @Component子组件自动获得对LocalParent LocalStorage实例的访问权限。
      LocalChild()
    }
  }
}

三、@LocalStorageProp和LocalStorage单向同步

在下面的示例中,CompA 组件和Child组件分别在本地创建了与storage的'PropA'对应属性的单向同步的数据,我们可以看到:

  • CompA中对this.storProp1的修改,只会在CompA中生效,并没有同步回storage;
  • Child组件中,Text绑定的storProp2 依旧显示47。
javascript 复制代码
let para: Record<string, number> = { 'PropA': 66 };
let storage: LocalStorage = new LocalStorage(para);
// 使LocalStorage可从@Component组件访问
@Entry(storage)
@Component
struct CompP {
  // @LocalStorageProp变量装饰器与LocalStorage中的'PropA'属性建立单向绑定
  @LocalStorageProp('PropA') storageProp1: number = 2;

  build() {
    Column({ space: 15 }) {
      Text(`@LocalStorageProp和LocalStorage单向同步 ${storage.get('PropA')}`).width('100%').height(40).fontStyle(FontStyle.Italic).fontSize(26)
      // 点击后从66开始加2,只改变当前组件显示的storageProp1,不会同步到LocalStorage中,即Child中的storageProp1不会改变
      Button(`CompP 中 LocalStorage ${this.storageProp1}`)
        .onClick(() => {
          this.storageProp1 += 2
        })
      ChildComp()
    }
  }
}

@Component
struct ChildComp {
  // @LocalStorageProp变量装饰器与LocalStorage中的'PropA'属性建立单向绑定
  @LocalStorageProp('PropA') storageProp2: number = 2;

  build() {
    Column({ space: 15 }) {
      // 初始化时显示的依然是66而不是2,当CompP改变时,当前storageProp2也不会改变,显示66
      Text(`ChildComp 中的LocalStorageProp: ${this.storageProp2} && ${storage.get('PropA')}`);
    }
  }
}

四、@LocalStorageLink和LocalStorage双向同步

@LocalStorageLink装饰的数据和LocalStorage双向同步的场景,还是上面的代码把@LocalStorageLink替换掉@LocalStorageProp就行了。

javascript 复制代码
Text(`@LocalStorageProp和LocalStorage单向同步 ${storage.get('PropA')} && ${this.storageProp1}`).width('100%').height(40).fontStyle(FontStyle.Italic).fontSize(24)

五、兄弟组件之间同步状态变量

通过@LocalStorageLink双向同步兄弟组件之间的状态。先看Parent自定义组件中发生的变化:

  • 点击"playCount ${this.playCount} dec by 1",this.playCount减1,修改同步回LocalStorage中,Child组件中的playCountLink绑定的组件会同步刷新;
  • 点击"countStorage ${this.playCount} incr by 1",调用LocalStorage的set接口,更新LocalStorage中"countStorage"对应的属性,Child组件中的playCountLink绑定的组件会同步刷新;
  • Text组件"playCount in LocalStorage for debug ${storage.get('countStorage')}"没有同步刷新,因为storage.get('countStorage')返回的是常规变量,常规变量的更新并不会引起Text组件的重新渲染。

Child自定义组件中的变化,playCountLink的刷新会同步回LocalStorage,并且引起兄弟组件和父组件相应的刷新。

javascript 复制代码
let ls: Record<string, number> = { 'countStorage': 88 }
let storageBro: LocalStorage = new LocalStorage(ls);

@Component
struct Bro {
  // 子组件实例的名字
  label: string ='';
  // 和LocalStorage中"countStorage"的双向绑定数据
  @LocalStorageLink('countStorage') playCountLink: number = 0;

  build() {
    Row() {
      Text(this.label).width(50).height(40).fontSize(12)
      Text(`Bro playCountLink(+2): ${this.playCountLink} && ${storageBro.get<number>('countStorage')}`)
        .onClick(() => {this.playCountLink += 2 }).width(200).height(40).fontSize(12)
    }.width(300).height(40)
  }
}
//若不传进来的话playCount的值不是用storageBro的来初始化, ${this.playCount}的值就会是99,而不是storage的88
@Entry(storageBro)
@Component
struct ParentBro {
  @LocalStorageLink('countStorage') playCount: number = 99;
  build() {
    Column() {
      Text(`ParentBro playCount(-1): ${this.playCount} && ${storageBro.get<number>('countStorage')} `)
        .onClick(() => {this.playCount -= 1;}).width('100%').height(40).fontSize(12)
      Text(`ParentBro playCount(+3): ${this.playCount} `)
        .onClick(() => {
          storageBro.set<number | undefined>('countStorage', Number(storageBro.get<number>('countStorage')) + 3);
        }).width('100%').height(40).fontSize(12)
      Bro({ label: 'ChildA' })
      Bro({ label: 'ChildB' })
      Text(`ParentBro ${storageBro.get<number>('countStorage')}`).width('100%').height(40).fontSize(12)
    }
  }
}

点击另外的按钮效果也是和点击这个ChildA一样,改动的都是联动的

七、将LocalStorage实例从UIAbility共享到一个或多个视图

LocalStorage的实例仅仅在一个@Entry装饰的组件和其所属的子组件(一个页面)中共享,如果希望其在多个视图中共享,可以在所属UIAbility中创建LocalStorage实例,并调用windowStage.loadContent传入,然后再需要使用时候通过LocalStorage.getShared() 实例来复用可共享了。下面以一个UIAbility中的多个组件共享一个LocalChildStorage来说明:

  • 在所属UIAbility中创建LocalStorage实例,并作为windowStage.loadContent的参数传入
javascript 复制代码
export default class EntryAbility extends UIAbility {

  param:Record<string,number> = {'PropAbility':68};
  storageAbility:LocalStorage = new LocalStorage(this.param);
  onWindowStageCreate(windowStage: window.WindowStage): void {
    // Main window is created, set main page for this ability
    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate');
    //把storageAbility传递过去
    windowStage.loadContent('pages/StatePageMgr', this.storageAbility);
  }
  ...
  }

在UI页面通过getShared接口获取通过loadContent共享的LocalStorage实例。LocalStorage.getShared()只在模拟器或者实机上才有效,在Previewer预览器中使用不生效。

  • 在所需要使用这个LocalStorage的组件的Page下通过LocalStorage.getShared()函数来获取实例(而不是自己new去创建哈),然后剩下的就是和传统的自己定义一个LocalStorage实例的使用方法一样了。如LocalPage.ets实现如下
javascript 复制代码
// 在通过getShared接口获取stage共享的LocalStorage实例
let storageFromAbility: LocalStorage = LocalStorage.getShared();

@Entry(storageFromAbility)
@Component
struct FromAbilityComp {
  @LocalStorageLink('PropAbility') propLink: number = 88;
  build() {
    Column() {
      Text(`LocalStorageFromAbility: ${this.propLink} && ${storageFromAbility.get<number>('PropAbility')}`).width('100%').height(36)
      Button('To Page B').width('100%').onClick(() => {
          router.pushUrl({
            url: 'pages/Index'
          })
        })
      }.height('100%')
    }
}

Index.ets

javascript 复制代码
let storageFromAbility2 = LocalStorage.getShared()

@Entry(storageFromAbility2)
@Component
struct IndexComp {
  @LocalStorageLink('PropAbility') propA: number = 66;

  build() {
    Row() {
      Column({space: 10}) {
        Text(`IndexComp this.propA: ${this.propA} && ${storageFromAbility2.get<number>('PropAbility')}`).width('100%').height(36)
          .fontSize(22)
          .fontWeight(FontWeight.Bold)
        Button("IndexComp: Change LocalStorageLink this.propA=100")
          .onClick(() => {
            this.propA = 100;
          }).height(36)
        Button("Back Prev page")
          .onClick(() => {
            router.back()
          })
      }
      .width('100%')
    }
  }
}

对于开发者更建议使用这个方式来构建LocalStorage的实例,并且在创建LocalStorage实例的时候就写入默认值,因为默认值可以作为运行异常的备份,也可以用作页面的单元测试。

相关推荐
whysqwhw2 小时前
鸿蒙分布式投屏
harmonyos
whysqwhw3 小时前
鸿蒙AVSession Kit
harmonyos
whysqwhw5 小时前
鸿蒙各种生命周期
harmonyos
whysqwhw6 小时前
鸿蒙音频编码
harmonyos
whysqwhw6 小时前
鸿蒙音频解码
harmonyos
whysqwhw6 小时前
鸿蒙视频解码
harmonyos
whysqwhw6 小时前
鸿蒙视频编码
harmonyos
ajassi20006 小时前
开源 Arkts 鸿蒙应用 开发(十八)通讯--Ble低功耗蓝牙服务器
华为·开源·harmonyos
前端世界7 小时前
在鸿蒙应用中快速接入地图功能:从配置到实战案例全解析
华为·harmonyos
江拥羡橙9 小时前
【基础-判断】HarmonyOS提供了基础的应用加固安全能力,包括混淆、加密和代码签名能力
安全·华为·typescript·harmonyos