数据存储
鸿蒙应用中的关于数据存储这块,分为应用状态管理存储还有一些数据持久化存储,不清楚概念的可以看我之前的前两篇文章,这边主要帮助大家区别一下状态存储和数据持久化存储的区别,避免概念和使用场景混淆。
1. localStorage和appStorage的区别,和对应的装饰器
LocalStorage
localStorage是页面级数据存储,在页面中创建实例,组件中使用@LocalStorageLink和@LocalStorageProp装饰器修饰对应的状态变量,绑定对应的组件使用比状态属性更灵活
//应用逻辑使用LocalStorage
let para: Record<string,number> = { 'PropA': 47 };
let storage: LocalStorage = new LocalStorage(para); // 创建新实例并使用给定对象初始化 , 创建实例存储数据
let propA: number | undefined = storage.get('PropA') // propA == 47 ,get()获取数据
//link():如果给定的propName在LocalStorage实例中存在,则返回与LocalStorage中propName对应属性的双向绑定数据。 (双向同步)
let link1: SubscribedAbstractProperty<number> = storage.link('PropA'); // link1.get() == 47
let link2: SubscribedAbstractProperty<number> = storage.link('PropA'); // link2.get() == 47
//prop():如果给定的propName在LocalStorage中存在,则返回与LocalStorage中propName对应属性的单向绑定数据。 (单向同步)
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
-
new LocalStorage(数据Object)
创建实例并存储数据 -
set方法,设置数据
-
get方法,获取数据
-
link方法,返回一个双向同步的变量
-
与UI逻辑中@LocalStorageLink装饰器类似
-
-
prop方法,返回单向同步的变量
-
与UI逻辑中@LocalStorageProp装饰器类似
-
//UI使用LocalStorage
//除了应用程序逻辑使用LocalStorage,还可以借助LocalStorage相关的两个装饰器@LocalStorageProp和@LocalStorageLink,在UI组件内部获取到LocalStorage实例中存储的状态变量。
// 创建新实例并使用给定对象初始化
let para: Record<string, number> = { 'PropA': 47 };
//这个变量一般就定义在某个@Entry组件的上方**
let storage: LocalStorage = new LocalStorage(para);
@Component
struct Child {// @LocalStorageLink变量装饰器与LocalStorage中的'PropA'属性建立双向绑定@LocalStorageLink('PropA') storageLink2: number = 1;
build() {Button(`Child from LocalStorage ${this.storageLink2}`)// 更改将同步至LocalStorage中的'PropA'以及Parent.storageLink1.onClick(() => {this.storageLink2 += 1})}
}
// 使LocalStorage可从@Component组件访问
@Entry(storage)//storage是上边定义的变量**
@Component
struct Parent {// @LocalStorageLink变量装饰器与LocalStorage中的'PropA'属性建立双向绑定@LocalStorageLink('PropA') storageLink1: number = 1;
build() {Column({ space: 15 }) {Button(`Parent from LocalStorage ${this.storageLink1}`) // initial value from LocalStorage will be 47, because 'PropA' initialized already.onClick(() => {this.storageLink1 += 1})// @Component子组件自动获得对CompA LocalStorage实例的访问权限。Child()}}
}
//上述代码:如果将LocalStorageLink改为LocalStorageProp就由双向变为了单向
-
new LocalStorage(数据Object)
创建实例并存储数据 -
父组件:
-
@Entry(LocalStorage实例)
, 将LocalStorage数据注册到页面中,使得页面内部可以使用数据 -
@LocalStorageLink
,将页面变量与数据进行双向绑定,父子组件都可以使用 -
@LocalStorageProp
,将页面变量与数据进行单向绑定,父子组件都可以使用
-
AppStorage
appStorage是进程级数据存储(==应用级的全局状态共享==),进程启动时自动创建了唯一实例,在各个页面组件中@StorageProp和@StorageLink装饰器修饰对应的状态变量。
//AppStorage是单例,它的所有API都是静态的
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
-
语法基本上与LocalStorage类似,只不过是静态方法
-
link双向,prop单向
AppStorage.setOrCreate('PropA', 47);
@Entry(storage)
@Component
struct CompA {@StorageLink('PropA') storageLink: number = 1;
build() {Column({ space: 20 }) {Text(`From AppStorage ${this.storageLink}`).onClick(() => {this.storageLink += 1})}}
}
-
将
@StorageLink
换为@StorageProp
,双向就变为单向的了 -
不建议使用@StorageLink , 其实就是因为AppStorage共享范围太多,更新效率低下,也可能造成不必要的更新
localStorage和appStorage数据存取都是在主线程进行的,且api只提供了同步接口,存取数据时要注意数据的大小。
-
关于存储时数据的大小问题
-
AppStorage没有大小限制,单条数据[k,v)],v也没有限制,但是不建议单条v大于1kb,大于1kb建议使用数据库。多条使用没有限制,会动态分配的。
-
LocalStorage底层实现是一个map,理论上没有大小限制。
-
-
参考链接:文档中心
2.LocalStorage,AppStorage,及PersistentStorage的具体实现及分别适用哪些场景
LocalStorage
LocalStorage是ArkTS为构建页面级别状态变量提供存储的内存内“数据库”。
-
应用程序可以创建多个LocalStorage实例,LocalStorage实例可以在页面内共享,也可以通过GetShared接口,实现跨页面、UIAbility实例内共享。
-
组件树的根节点,即被@Entry装饰的@Component,可以被分配一个LocalStorage实例,此组件的所有子组件实例将自动获得对该LocalStorage实例的访问权限。
-
被@Component装饰的组件最多可以访问一个LocalStorage实例和AppStorage,未被@Entry装饰的组件不可被独立分配LocalStorage实例,只能接受父组件通过@Entry传递来的LocalStorage实例。一个LocalStorage实例在组件树上可以被分配给多个组件。
-
LocalStorage中的所有属性都是可变的。
应用程序决定LocalStorage对象的生命周期。当应用释放最后一个指向LocalStorage的引用时,比如销毁最后一个自定义组件,LocalStorage将被JS Engine垃圾回收。
LocalStorage根据与@Component装饰的组件的同步类型不同,提供了两个装饰器:
-
@LocalStorageProp:@LocalStorageProp装饰的变量和与LocalStorage中给定属性建立单向同步关系。
-
@LocalStorageLink:@LocalStorageLink装饰的变量和在@Component中创建与LocalStorage中给定属性建立双向同步关系。
使用场景:
-
应用逻辑使用LocalStorage
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实例中存储的状态变量。
AppStorage
AppStorage是在应用启动的时候会被创建的单例。它的目的是为了提供应用状态数据的中心存储,这些状态数据在应用级别都是可访问的。AppStorage将在应用运行过程保留其属性。属性通过唯一的键字符串值访问。
AppStorage可以和UI组件同步,且可以在应用业务逻辑中被访问。
AppStorage支持应用的主线程内多个UIAbility实例间的状态共享。
AppStorage中的属性可以被双向同步,数据可以是存在于本地或远程设备上,并具有不同的功能,比如数据持久化(详见PersistentStorage)。这些数据是通过业务逻辑中实现,与UI解耦,如果希望这些数据在UI中使用,需要用到@StorageProp和@StorageLink。
使用场景:
-
从应用逻辑使用AppStorage
AppStorage是单例,它的所有API都是静态的,使用方法类似于中LocalStorage对应的非静态方法。
AppStorage.setOrCreate('PropA', 47); let storage: LocalStorage = new LocalStorage(); storage.setOrCreate('PropA',17); let propA: number | undefined = AppStorage.get('PropA') // propA in AppStorage == 47, propA in LocalStorage == 17 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 storage.get<number>('PropA') // == 17 storage.set('PropA', 101); storage.get<number>('PropA') // == 101 AppStorage.get<number>('PropA') // == 49 link1.get() // == 49 link2.get() // == 49 prop.get() // == 49
-
从UI内部使用AppStorage和LocalStorage
@StorageLink变量装饰器与AppStorage配合使用,正如@LocalStorageLink与LocalStorage配合使用一样。此装饰器使用AppStorage中的属性创建双向数据同步。
PersistentStorage
PersistentStorage将选定的AppStorage属性保留在设备磁盘上。应用程序通过API,以决定哪些AppStorage属性应借助PersistentStorage持久化。UI和业务逻辑不直接访问PersistentStorage中的属性,所有属性访问都是对AppStorage的访问,AppStorage中的更改会自动同步到PersistentStorage。
PersistentStorage和AppStorage中的属性建立双向同步。应用开发通常通过AppStorage访问PersistentStorage,另外还有一些接口可以用于管理持久化属性,但是业务逻辑始终是通过AppStorage获取和设置属性的
使用场景:
从AppStorage中访问PersistentStorage初始化的属性
在PersistentStorage之前访问AppStorage中的属性
在PersistentStorage之后访问AppStorage中的属性
-
参考链接:文档中心
3.首选项异步存储在并发场景上的注意事项
首选项preference提供了异步存储接口,首选项的数据存储维度分为内存和沙盒,为避免内存过大导致报警,要控制首选项数据存储的数据,可以使用await来控制并发存储的问题。
4.数据存储怎么存?都用过什么数据存储?如appstorage、数据库等,这个会结合项目介绍问,可以在多看看其他存储方式及使用
-
用户首选项实现数据持久化(Perferences)
用户首选项为应用提供Key-Value键值型的数据处理能力,支持应用持久化轻量级数据,并对其修改和查询。当用户希望有一个全局唯一存储的地方,可以采用用户首选项来进行存储。Preferences会将该数据缓存在内存中,当用户读取的时候,能够快速从内存中获取数据,当需要持久化时可以使用flush接口将内存中的数据写入持久化文件中。Preferences会随着存放的数据量越多而导致应用占用的内存越大,因此,Preferences不适合存放过多的数据,适用的场景一般为应用保存用户的个性化设置(字体大小,是否开启夜间模式)等。
-
键值型数据库实现数据持久化
键值型数据库存储键值对形式的数据,当需要存储的数据没有复杂的关系模型,比如存储商品名称及对应价格、员工工号及今日是否已出勤等,由于数据复杂度低,更容易兼容不同数据库版本和设备类型,因此推荐使用键值型数据库持久化此类数据。
-
关系型数据库实现数据持久化
关系型数据库基于SQLite组件,适用于存储包含复杂关系数据的场景,比如一个班级的学生信息,需要包括姓名、学号、各科成绩等,又或者公司的雇员信息,需要包括姓名、工号、职位等,由于数据之间有较强的对应关系,复杂程度比键值型数据更高,此时需要使用关系型数据库来持久化保存数据。
-
AppStorage:应用全局的UI状态存储
AppStorage是应用全局的UI状态存储,是和应用的进程绑定的,由UI框架在应用程序启动时创建,为应用程序UI状态属性提供中央存储。
-
LocalStorage:页面级UI状态存储
LocalStorage是页面级的UI状态存储,通过@Entry装饰器接收的参数可以在页面内共享同一个LocalStorage实例。LocalStorage支持UIAbility实例内多个页面间状态共享。
-
参考链接:
-
文档中心
-
文档中心
-