HarmonyOS-基础之状态数据共享
1、LocalStorage
页面级UI状态存储,通常用于UIAbility内、页面间的状态共享
(1) 先抛出一个疑问
- 疑问:如何实现一个页面中所有组件的数据共享?
- 解决:使用LocalStorage技术
(2) 页面级状态内存存储
- 只能在一个页面中的所有组件中共享
- 退出应用不存在
(3) 相关API
LocalStorage({name, value})
:构造函数@Entry(storage)
: 将storage绑定到页面根组件@LocalStorageLink(name)
:实现组件状态与LocalStorage的双向同步@LocalStorageProp(name)
:实现LocalStorage到组件状态的单向同步
(4) 案例
父组件
// 父组件
// @ts-nocheck
import Child01 from './components/Child01'
/**
* 使用LocalStorage实现一个页面中所有组件的数据共享
* - 页面级别
* - 页面级状态内存存储
* - 只能在一个页面中的所有组件中共享
* - 退出应用不存在
*/
let storage = new LocalStorage({ 'name': 'zs' })
let propName = storage.get('name')
let link1 = storage.link('name')
let link2 = storage.link('name')
let prop = storage.prop('name')
console.log("set前 propName:" + propName)
console.log("set前 link1:" + link1.get())
console.log("set前 link2:" + link2.get())
console.log("set前 prop:" + prop.get())
propName = 'tq'
link1.set('ls')
link2.set('ww')
prop.set('zl')
console.log("set后 propName:" + propName)
console.log("set后 link1:" + link1.get())
console.log("set后 link2:" + link2.get())
console.log("set后 prop:" + prop.get())
// 创建新实例并使用给定对象初始化
let count = new LocalStorage({ 'count': 1 })
// 使LocalStorage可从@Component组件访问
@Entry(count)
@Component
struct Index {
// @LocalStorageProp 变量装饰器与 LocalStorage 中的 'count' 属性建立单向绑定
@LocalStorageProp('count') countProp: number = 0
// @LocalStorageLink 变量装饰器与 LocalStorage 中的 'count' 属性建立双向绑定
@LocalStorageLink('count') countLink: number = 0
build() {
Row() {
Column({ space: 10 }) {
Text('父组件')
Text('countProp:' + this.countProp)
.fontSize(20)
.fontWeight(FontWeight.Bold)
Text('countLink:' + this.countLink)
.fontSize(20)
.fontWeight(FontWeight.Bold)
// 点击按钮后,只改变当前组件显示的 count 不会同步到 LocalStorage 中
Button('count + 1').onClick((event: ClickEvent) => {
this.countProp++
})
// 点击按钮后,改变当前组件显示的 count 会同步到 LocalStorage 中,子组件也会跟着一起改变
Button('count + 1').onClick((event: ClickEvent) => {
this.countLink++
})
// 扩展写法
Button('通过storage.set方法进行修改').onClick((event: ClickEvent) => {
// count.set('count',999)
let countInner = count.get('count')
// countInner = 100
let link1 = count.link('count')
// link1.set(101)
let link2 = count.link('count')
// link2.set(102)
let prop = count.prop('count')
// prop.set(103)
})
// 子组件
Child01()
}.width('100%')
.border({ width: 1, color: Color.Red, style: BorderStyle.Dashed })
}.height('100%')
}
}
子组件
// 子组件
@Component
export default struct Index {
// @LocalStorageProp 变量装饰器与 LocalStorage 中的 'count' 属性建立单向绑定
@LocalStorageProp('count') countProp: number = 10
// @LocalStorageLink 变量装饰器与 LocalStorage 中的 'count' 属性建立双向绑定
@LocalStorageLink('count') countLink: number = 20
build() {
Row() {
Column({ space: 10 }) {
Text('子组件')
Text('countProp :' + this.countProp)
.fontSize(20)
.fontWeight(FontWeight.Bold)
Text('countLink :' + this.countLink)
.fontSize(20)
.fontWeight(FontWeight.Bold)
// 点击按钮后,只改变当前组件显示的 count 不会同步到 LocalStorage 中
Button('countProp - 1').onClick((event: ClickEvent) => {
this.countProp--
})
// 点击按钮后,改变当前组件显示的 count 会同步到 LocalStorage 中,子组件也会跟着一起改变
Button('countLink - 1').onClick((event: ClickEvent) => {
this.countLink--
})
}.width('100%')
.border({ width: 1, color: Color.Blue, style: BorderStyle.Dotted })
}
}
}
从上面的例子可以看出:
- 我们在父组件定义的 count 在子组件中通过@LocalStorageProp进行装饰,当修改 count 时,只有父组件当前页面数据会发生改变 也就是数据“单向流动”
- 而我们在父组件定义的 count 在子组件中通过 @LocalStorageLink 进行装饰,当修改 count 时,不仅父组件当前页面数据会发生改变,子组件也会发生改变,也就是数据“双向流动”
2、AppStorage
特殊的单例LocalStorage对象,由UI框架在应用程序启动时创建,为应用程序UI状态属性提供中央存储
(1) 先抛出一个疑问
- 疑问:如何实现整个应用中所有页面的所有组件的数据共享?
- 解决:使用AppStorage技术
(2) 应用全局的UI状态存储
- 退出应用后数据还存在,再次启动后,还能操作到AppStorage中的数据
(3) 相关API
PersistentStorage.PersistProp<T>(name: string, value: T)
: 初始化数据@StorageLink/@StorageProp
装饰器:获取同步数据
(4) 注意
- 持久化的number类型数据,在应用退出后,再读取得到的string类型
(5) 案例
父组件
import Child02 from './components/Child02'
let appStorage = AppStorage.SetOrCreate('name', 'zs')
let name: string = AppStorage.Get('name')
let link1: SubscribedAbstractProperty<string> = AppStorage.Link('name')
let link2: SubscribedAbstractProperty<string> = AppStorage.Link('name')
let prop: SubscribedAbstractProperty<string> = AppStorage.Prop('name')
console.log('set前的name : ' + name)
console.log('set前的link1 : ' + link1.get())
console.log('set前的link2 : ' + link2.get())
console.log('set前的prop : ' + prop.get())
name = 'ls'
link1.set('ww')
link2.set('zl')
prop.set('tq')
console.log('set后的name : ' + name)
console.log('set后的link1 : ' + link1.get())
console.log('set后的link2 : ' + link2.get())
console.log('set后的prop : ' + prop.get())
AppStorage.SetOrCreate('count', 10)
@Entry
@Component
struct Index {
// @StorageProp 变量装饰器与 AppStorage 中的 'count' 属性建立单向绑定
@StorageProp('count') countProp: number = 0
// @StorageLink 变量装饰器与 AppStorage 中的 'count' 属性建立双向绑定
@StorageLink('count') countLink: number = 0
build() {
Row() {
Column({ space: 10 }) {
Text('父组件')
Text('countProp : ' + this.countProp)
.fontSize(20)
.fontWeight(700)
Text('countList : ' + this.countLink)
.fontSize(20)
.fontWeight(700)
Child02()
// 点击按钮后,只改变当前组件显示的 count 不会同步到 LocalStorage 中
Button('countProp + 1').onClick((event: ClickEvent) => {
this.countProp++
})
// 点击按钮后,改变当前组件显示的 count 会同步到 LocalStorage 中,子组件也会跟着一起改变
Button('countLink + 1').onClick((event: ClickEvent) => {
this.countLink++
})
Button('通过storage.set方法进行修改').onClick((event: ClickEvent) => {
let count: number = AppStorage.Get('count')
// count = 100
let link1: SubscribedAbstractProperty<number> = AppStorage.Link('count')
// link1.set(101)
let link2: SubscribedAbstractProperty<number> = AppStorage.Link('count')
// link2.set(102)
let prop: SubscribedAbstractProperty<number> = AppStorage.Prop('count')
prop.set(103)
})
}.width('100%')
.border({ width: 1, color: Color.Red, style: BorderStyle.Solid })
.padding(10)
}.height('100%')
}
}
子组件
@Component
export default struct Index {
// @StorageProp 变量装饰器与 AppStorage 中的 'count' 属性建立单向绑定
@StorageProp('count') countProp: number = 101
// @StorageLink 变量装饰器与 AppStorage 中的 'count' 属性建立双向绑定
@StorageLink('count') countLink: number = 102
build() {
Row() {
Column({ space: 10 }) {
Text('子组件')
Text('countProp : ' + this.countProp)
.fontSize(20)
.fontWeight(700)
Text('countLink : ' + this.countLink)
.fontSize(20)
.fontWeight(700)
// 点击按钮后,只改变当前组件显示的 count 不会同步到 LocalStorage 中
Button('countProp - 1').onClick((event: ClickEvent) => {
this.countProp--
})
// 点击按钮后,改变当前组件显示的 count 会同步到 LocalStorage 中,子组件也会跟着一起改变
Button('countLink - 1').onClick((event: ClickEvent) => {
this.countLink--
})
}.width('100%')
.border({ width: 1, color: Color.Pink, radius: { topLeft: 5, topRight: 10, bottomLeft: 15, bottomRight: 20 } })
.padding(10)
}
}
}
从上面的例子可以看出:
- 我们在父组件定义的 count 在子组件中通过@StorageProp进行装饰,当修改 count 时,只有父组件当前页面数据会发生改变 也就是数据“单向流动”
- 而我们在父组件定义的 count 在子组件中通过 @StorageLink 进行装饰,当修改 count 时,不仅父组件当前页面数据会发生改变,子组件也会发生改变,也就是数据“双向流动”
3、PersistentStorage
LocalStorage和AppStorage都是运行时的内存,但是在应用退出再次启动后,依然能保存选定的结果,那便需要用到PersistentStorage
PersistentStorage是应用程序中的可选单例对象。此对象的作用是持久化存储选定的AppStorage属性,以确保这些属性在应用程序重新启动时的值与应用程序关闭时的值相同。
(1) 先抛出一个疑问
- 疑问:如何实现退出应用后状态数据还存在?
- 解决:使用PersistentStorage技术
(2) 应用全局持久化状态存储
- 退出应用后数据还存在,再次启动后,还能操作到AppStorage中的数据
(3) 相关API
PersistentStorage.PersistProp<T>(name: string, value: T)
: 初始化数据@StorageLink/@StorageProp
装饰器:获取同步数据
(4) 注意
PersistentStorage允许的类型和值有:
- number, string, boolean, enum 等简单类型。
- 可以被JSON.stringify()和JSON.parse()重构的对象。例如Date, Map, Set等内置类型则不支持,以及对象的属性方法不支持持久化。
特别注意:
-
PersistentStorage的持久化变量最好是小于2kb的数据,不要大量的数据持久化,因为PersistentStorage写入磁盘的操作是同步的,大量的数据本地化读写会同步在UI线程中执行,影响UI渲染性能。如果开发者需要存储大量的数据,建议使用数据库api。
-
PersistentStorage只能在UI页面内使用,否则将无法持久化数据。
-
持久化的number类型数据,在应用退出后,再读取得到的string类型
(5) 案例
/**
* 持久化数据
*/
PersistentStorage.PersistProp('count', 999999)
@Entry
@Component
struct Index {
@StorageProp('count') countProp: number = 0
@StorageLink('count') countLink: number = 0
build() {
Row() {
Column({space:10}) {
Text('父组件')
Text('countProp : ' + this.countProp)
.fontSize(20)
.fontWeight(700)
Text('countLink : ' + this.countLink)
.fontSize(20)
.fontWeight(700)
Button('countProp + 1').onClick((event: ClickEvent) => {
this.countProp ++
})
}.width('100%')
.border({ width: 1, color: Color.Red, style: BorderStyle.Solid })
}.height('100%')
}
}
这玩意儿得在真机上测试
能看到这儿,说明你很不错了,继续加油!朋友,未来无限可能
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
2022-04-19 MongoDB入门