HarmonyOS 鸿蒙Next ArkUI状态管理:@StorageLink装饰器之我的一些经验

发布于 1周前 作者 nodeper 最后一次编辑是 5天前 来自 鸿蒙OS

HarmonyOS 鸿蒙Next ArkUI状态管理:@StorageLink装饰器之我的一些经验

在声明式UI编程范式中,UI是应用程序状态的函数,开发人员通过修改当前应用程序状态来更新相应的UI界面。

比如@State装饰器,当我们在组件内修改@State变量后,将会自动调用组件的build方法刷新UI


@Entry @Component struct EntryComponent { @State str: string = ‘hello’ build() { Column({ space: 20 }) { Text(this.str).fontSize(30) .onClick(() => { this.str = ‘hello HarmonyOS’ }) } } }当我们点击Text时,onClick事件被触发,此时@State变量str的值被修改为"hello HarmonyOS",build()方法会被重新调用,刷新Text的文本内容,Text的文本内容由"hello"变为"hello HarmonyOS"

@State是我们最常用的UI状态管理的装饰器,用起来很方便,但它同样有这样那样的缺点,比如只能在组件内使用,不能和子组件同步,不支持object,无法持久化。

HarmonyOS为我们提供了@StorageLink装饰器,这个装饰器的功能很强大,但目前在官方文档中描述很少,只能找到下面这一段:

@StorageLink装饰器

组件通过使用@StorageLink(key)装饰的状态变量,将于AppStorage建立双向数据绑定,keyAppStorage中的属性键值。当创建包含@StorageLink的状态变量的组件时,该状态变量的值将使用AppStorage中的值进行初始化,不允许使用本地初始化。在UI组件中对@StorageLink的状态变量所做的更改将同步到AppStorage,并从AppStorage同步到任何其他绑定实例中,如PersistentStorage或其他绑定的UI组件。

这段内容的意思应该是@StorageLink要和AppStorage配合使用,但是AppStorage接口目前还有点问题,上面的功能完全无法体现。

经过多次测试后,我总结出了以下几个@StorageLink的特点:

  • @StorageLink(key) 装饰的变量是组件的状态数据,当这些状态数据被修改时,将会调用所在组件的build方法进行UI刷新。
  • 一个@StorageLink装饰的数据被修改后,所有相同key的@StorageLink变量都会随之改变,不管你是哪一个页面。
  • @StorageLink的作用域是全局的,在应用程序运行期间,在任何页面的任何组件中都可以通过键值访问它的值,它的生命周期取决于应用程序的生命周期。
  • 双向同步:在父组件中修改@StorageLink变量会同步到子组件中相同key的@StorageLink变量,反之亦然。
  • 所有@StorageLink变量必须初始化。
  • @StorageLink支持的数据类型同@State类似,但是它还支持object
  • @StorageLink可通过PersistentStorage接口持久化
  • 所有相同key的@StorageLink变量,他们的初始值以第一个初始化的值为准

因为文档中没有给@StorageLink起名字,所以根据它的特性,我暂时叫它"组件全局状态"

先来看一个最简单的例子

数据绑定


@Entry @Component struct EntryComponent { @StorageLink(‘key1’) link1: string = ‘hello’ @StorageLink(‘key1’) link2: string = ‘hello’ build() { Column({ space: 20 }) { Text(this.link1).fontSize(30) .onClick(() => { this.link2 = ‘你好’ }) } } } 运行应用,Text的内容显示的是link1的值"hello"

image.png

点击Text组件后,link2的值被修改为"你好",此时link1的值也随之变为"你好",build方法被调用,刷新UI,如图:

image.png

从上例中可以看出,link1和link2的值是绑定的,不管你修改哪一个值,另一个都会同步修改,同时build方法被调用。

再来看第二个例子:

双向同步


@Component struct ChildComponent { @StorageLink(‘key1’) link: string = ‘’ build() { Text(‘child:’ + this.link).fontSize(30).fontColor(Color.Red) .onClick(() => { this.link = ‘你好,鸿蒙’ }) } }

@Entry @Component struct EntryComponent { @StorageLink(‘key1’) link1: string = ‘hello’ build() { Column({ space: 20 }) { Text(‘parent:’ + this.link1).fontSize(30).fontColor(Color.Blue) .onClick(() => { this.link1 = ‘hello HarmonyOS’ })

  ChildComponent()
}
.width(<span class="hljs-string"><span class="hljs-string">'100%'</span></span>)
.alignItems(HorizontalAlign.Center)

} }<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>

我们在入口组件中绑定了键为"key1"的@StorageLink变量link1,在子组件中绑定了相同key的变量link2

当点击父组件的文本时,link1的值被修改,子组件的link2同步变动,父组件和子组件的build方法被调用

当点击子组件的文本时,link2的值被修改,父组件的link1同步变动,子组件和父组件的build方法被调用

animator032.gif

 

第三个例子

页面间通信


// index.ets @Entry @Component struct EntryComponent { @StorageLink(‘key1’) link1: string = ‘hello’

build() { Column({ space: 20 }) { Text(this.link1).fontSize(50) .onClick(() => { this.link1 = ‘hello HarmonyOS’ }) // 跳转到page2 Navigator({ target: ‘pages/page2’}) { Button(‘go to page2’).fontSize(30) } } .width(‘100%’) .alignItems(HorizontalAlign.Center) } }<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>

右键new -> eTS Page,创建一个名为page2的页面,代码如下


// page2.ets @Entry @Component struct Page2 { @StorageLink(‘key1’) link: string = ‘’

build() { Column({ space: 20 }) { Text(this.link).fontSize(30) .onClick(() => { this.link = “你好鸿蒙” }) // 返回index页面 Navigator({ target: ‘pages/index’, type: NavigationType.Back }) { Button(‘back’).fontSize(30) } } .width(‘100%’) } }<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>

运行效果如图:

animator031.gif

第四个例子

持久化

@StorageLink是可以被PersistentStorage接口持久化的,使用起来也非常简单,只要一行就OK了


PersistentStorage.PersistProp(“key1”, “hello”)<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>

上面的第一个参数key1就是我们想要持久化的@StorageLink的key,第二个参数是默认值

完整代码如下


PersistentStorage.PersistProp(“key1”, “hello”)

@Entry @Component struct EntryComponent { @StorageLink(‘key1’) link1: string = ‘hello’ @StorageLink(‘key1’) link2: string = ‘hello’ build() { Column({ space: 20 }) { Text(this.link1).fontSize(50) .onClick(() => { this.link2 = ‘你好,鸿蒙!’ }) } } }<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>

运行效果:

animator033.gif

从示例中我们看到即使应用程序的生命周期结束了,@StorageLink绑定的变量依然是被修改后的值,说明该变量确实被持久化了。

@StorageLink装饰器的功能是很全面的,但是个人觉得最重要的还是它能被PersistentStorage持久化,这一点是@State@Link@Prop这些都不能比的。

12 回复
这么用心的帖子到现在每一个评论,文章好评好评!!
多谢楼主愿意分享自己的经验,赞

找HarmonyOS工作还需要会Flutter的哦,有需要Flutter教程的可以学学大地老师的教程,很不错,B站免费学的哦:https://www.bilibili.com/video/BV1S4411E7LY/?p=17

帖子写得真好,太用心了,学习了,谢谢。

有要学HarmonyOS AI的同学吗,联系我:https://www.itying.com/goods-1206.html

个人认为 StorageLink是用来组件跟应用全局状态进行绑定同步的一个api,比较适合的场景是,获取 应用全局的一些状态,比如用户信息、公共数据信息(地址信息、行业信息等),不能随便使用,大量使用会导致状态出现混乱
必须给老师点个赞👍👍👍
我的理解是 页面间通信,ability间的通信,[@StroageLink](/user/StroageLink)是双向同步,一个页面改了,其他页面都会改变。[@storageProp](/user/storageProp)是单向通信,使用[@StorageProp](/user/StorageProp)接收的AppStoreage数据,在当前页面改变后,不会影响其他页面。

在HarmonyOS鸿蒙Next的ArkUI状态管理中,@StorageLink装饰器是一个强大的工具,它能够将组件的状态与全局存储(如GlobalContext中的变量)进行绑定,实现数据的双向绑定与自动同步。以下是我使用@StorageLink的一些实战经验:

  1. 简化状态管理:通过@StorageLink,你可以直接在模板中访问和修改全局状态,无需手动编写大量的状态更新逻辑,从而简化了状态管理。

  2. 提高代码可读性:使用@StorageLink装饰的变量在模板中清晰明了,其他开发者可以更容易地理解数据流向和状态更新逻辑。

  3. 性能优化@StorageLink通过底层优化,减少了不必要的状态更新和渲染,从而提高了应用的性能。

  4. 注意事项:在使用@StorageLink时,需要确保全局存储的变量类型与组件中声明的类型一致,以避免类型不匹配导致的错误。此外,当全局状态发生变化时,需要确保相关的组件能够正确响应并更新视图。

总之,@StorageLink是ArkUI状态管理中的一个重要工具,它能够帮助开发者更高效地管理应用状态。如果在使用过程中遇到问题,建议检查全局存储的变量类型、状态更新逻辑以及组件的响应机制。如果问题依旧没法解决请联系官网客服,官网地址是:https://www.itying.com/category-93-b0.html。

回到顶部