Harmony Next - UI 级状态管理 LocalStorage

引子

在现代的声明式开发的 UI 框架中,一般来说状态管理都是应对与变量的。也就是变量是和组件相关联的。比如鸿蒙系统中的 @State@Prop 等。

假设我们一个页面中有一个父组件 Index 和两个子组件 ChildA、 ChildB。如果 ChildA 组件想使用 Index 的数据,那我们就需要在构造 ChildA 组件的时候将数据传递进去,ChildB 想要使用也是如此,示例代码如下:

scss 复制代码
@Entry
@Component
struct Index {
  @State content: string = "这是内容显示";
  build() {
    Column({space: 10}) {
      Text(`这是父组件的内容:${this.content}`)
      ChildA({childAContent: this.content})
      ChildB({childBContent: this.content})

      Button("修改父组件内容").onClick(() => {
        this.content = "hello, harmony!"
      })
    }
    .height('100%')
    .width('100%')
  }
}

@Component
struct ChildA {
  @Prop childAContent: string = '';
  build() {
    Row() {
      Text("ChildA - ")
      Text(`${this.childAContent}`)
      Button("修改ChildA 组件的内容").onClick(() => {
        this.childAContent = "hello, ChildA!"
      })
    }
  }
}

@Component
struct ChildB {
  @Link childBContent: string;
  build() {
    Row() {
      Text("ChildB - ")
      Text(`${this.childBContent}`)
      Button("修改ChildB 组件的内容").onClick(() => {
        this.childBContent = "hello, ChildB!"
      })
    }
  }
}

上述代码定义了页面 Index 和两个自定义组件 ChildA 和 ChildB。Index 有一个 content 的变量来显示内容。ChildA 和 ChildB 分别需要显示 Index 组件的内容,所以在两个组件初始化的时候将 content 变量传递了进去。ChildA 的 childAContent 由 @Prop 修饰,从父到子单向同步;而 ChildB 的 childBContent 由 @Link 修饰,父子双向同步。效果图如下:

点击效果:点击修改父组件和 ChildB 组件的按钮,三个组件的内容全部会变化,点击 ChildA 组件的按钮只有 ChildA 组件的内容变化。

如果使用了 LocalStorage 来实现,我们就可以不用再显示的进行参数传递了。下面来看一下用 LocalStorage 如何实现上面的需求。

LocalStorage 的使用

LocalStorage 使用用法也是比较简单的,我们只需要初始化一个 LocalStorage 实例,然后在子组件中使用以下两个装饰器修饰即可:

  • @LocalStorageProp(key):类似于 @Prop,父子单向同步,子组件的修改不会同步回父组件。
  • @LocalStorageLink(key):类似于 @Link,父子双向同步。

下面的示例代码就是用 LocalStorage 实现引言中的代码效果:

scss 复制代码
// 初始化 LocalStorage
const storage: LocalStorage = new LocalStorage();
storage.setOrCreate("content", "");

@Entry(storage)
@Component
struct Index {
  @LocalStorageLink("content") content: string = "这是内容显示";
  build() {
    Column({space: 10}) {
      Text(this.content)
      ChildA()
      ChildB()

      Button("修改父组件内容").onClick(() => {
        this.content = "hello, harmony!"
      })
    }
    .height('100%')
    .width('100%')
  }
}

@Component
struct ChildA {
  @LocalStorageProp("content") childAContent: string = "";
  build() {
    Row() {
      Text("ChildA - ")
      Text(this.childAContent)
      Button("修改ChildA 组件的内容").onClick(() => {
        this.childAContent = "hello, ChildA!"
      })
    }
  }
}

@Component
struct ChildB {
  @LocalStorageLink("content") childBContent: string = "";
  build() {
    Row() {
      Text("ChildB - ")
      Text(this.childBContent)
      Button("修改ChildB 组件的内容").onClick(() => {
        this.childBContent = "hello, ChildB!"
      })
    }
  }
}

上面的代码实现的效果是和使用 @Porp@Link 的例子一样的,但我们无需再给两个子组件进行参数传递。只需要使用 @LocalStorageProp 或者 @LocalStorageLink 装饰器,传入相应的 key 即可获取 LocalStorage 相应的值。

需要注意的是,只有组件树的根节点才能被分配 LocalStorage 实例,也就是只有 @Entry 修饰的 @Component 组件才能被分配一个实例,如上述代码中的 Index,它的子组件自动会获得 LocalStorage 实例的访问权限,如上述代码中的 ChildA 组件和 ChildB 组件。

还有就是 LocalStorage 的中存储的字段一经声明后续是无法变更类型的,比如下面的代码就是错误的:

scss 复制代码
@Component
struct ChildB {
  @LocalStorageLink("content") childBContent: number = 0;
  build() {
    Row() {
      Text("ChildB - ")
      Button("修改ChildB 组件的内容").onClick(() => {
        this.childBContent = 10
      })
    }
  }
}

因为 content 字段声明时是字符串类型,这里改为数字类型是不可以的,点击了按钮其他组件的内容也不会跟着更新。

多页面共享 LocalStorage 实例

上面举例代码中是一个页面对应一个 LocalStorage 实例,我们也可以通过在 UIAbility 中传入一个 LocalStorage 实例,从而实现多个页面共享一个实例。通过以下三步即可实现:

  • EntryAbility 初始化LocalStorage 实例
  • loadContent 接口中将实例传入进去
  • 在相应的页面通过 getShared() 获取实例

示例代码如下:

typescript 复制代码
// EntryAbility 中的 onWindowStageCreate 中
// 第一步:初始化
const storage = new LocalStorage();
storage.setOrCreate("content", "初始值")
// 第二步:通过 loadContent 接口传入
windowStage.loadContent('pages/Index', storage, (err) => {
  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.');
});


// Index.ets 第三步 获取实例
const storage: LocalStorage = LocalStorage.getShared();
@Entry(storage)
@Component
struct Index {}
相关推荐
lilian23335 分钟前
HarmonyOS 7 新特性(二十五)|智慧手势:意图识别与误触治理
华为·harmonyos
贾伟康3 小时前
【时光清单|14】HarmonyOS ArkTS 主导航实战:统一页面入口、返回路径和参数校验
harmonyos·arkts·arkui·navigation·路由管理
贾伟康7 小时前
【时光清单|05】HarmonyOS ArkTS 倒计时卡片实战:适配 2x2、2x4 与 4x4 多尺寸
harmonyos·arkts·arkui·服务卡片·formextensionability
小雨青年8 小时前
【HarmonyOS 7 悬浮页签深度实战】07 折叠屏、平板与宽窗口的布局适配
华为·harmonyos
熊猫钓鱼>_>10 小时前
鸿蒙AI Agent新范式:从“对话式辅助”到“工程化代理”的Harness架构实战解析
人工智能·笔记·学习·华为·架构·harmonyos
大锅盖110 小时前
HarmonyOS ArkUI 非遗纹样素材平台设计复盘:朱砂宣纸与鎏金黛蓝的文化守护
华为·harmonyos
2501_9197490310 小时前
华为鸿蒙管理学习生活与工作APP—小羊管理
学习·华为·生活·harmonyos·鸿蒙
贾伟康11 小时前
【时光清单|20】HarmonyOS ArkTS AppGallery 发布复查实战:核对包名、版本、设备、素材和离线声明
harmonyos·arkts·agc·appgallery·发布复查
lilian23312 小时前
HarmonyOS 7 新特性(二十三)|hiprofiler + hidumper:跨堆内存分析
华为·harmonyos
嵌入之梦12 小时前
鸿蒙OS IoT设备开发之基础 —— 外设驱动
物联网·华为·harmonyos