简介

        在日常的开发过程中,状态管理一直是一个很重要的话题,在React中可以使用Redux,在Vue中可以使用Vuex和Pinia,在Flutter中可以使用setState来管理状态。总之状态管理不光是前端,移动端中都是占据着重要的地位,因此在鸿蒙的开发中,状态管理也是必不可少的,接下来我们将通过LocalStorage(页面级),AppStorage(应用级)和Preferences(数据持久化)来进行学习。

使用方法

LocalStorage

LocalStorage是页面级的UI状态存储,通过@Entry装饰器接受的参数可以实现页面的共享同一个LocalStorage实例,它支持UIAbility实例内多个页面间的状态共享。一般使用的装饰器有两个@LocalstorageProp(单向同步)和@LocalStorageLink(双向同步)。

@LocalStorageProp

let para: Record<string, number> = { 'PropA': 47 };
let storage: LocalStorage = new LocalStorage(para);

@Entry(storage)
@Component
struct Parent {
  @LocalStorageProp('PropA') storageProp1: number = 1;

  build() {
    Column({ space: 15 }) {
      Button(`Parent from LocalStorage ${this.storageProp1}`)
        .onClick(() => {
          this.storageProp1 += 1;
        })
      Child()
    }
    .padding({ top: 40, left: 20, right: 20 })
  }
}

@Component
struct Child {
  @LocalStorageProp('PropA') storageProp2: number = 2;

  build() {
    Column({ space: 15 }) {
      Text(`Parent from LocalStorage ${this.storageProp2}`)
    }
  }
}

通过上面的代码我们可以看到,点击按钮的话,只是父组件是加1的,子组件没有发生变化,由此可见数据的传输方向是单向的。

  @LocalStorageLink    

// 构造LocalStorage实例
let para: Record<string, number> = { 'PropA': 47 };
let storage: LocalStorage = new LocalStorage(para);

@Entry(storage)
@Component
struct Parent {
  @LocalStorageLink('PropA') storageLink1: number = 1;

  build() {
    Column({ space: 20 }) {
      Button(`点我加一哦~~`)
        .onClick(() => {
          this.storageLink1 += 1;
        })
      Text('父组件的值是:' + this.storageLink1)
      Child()
    }
    .padding({
      top: 40,
      left: 20,
      right: 20
    })
  }
}


@Component
struct Child {
  @LocalStorageLink('PropA') storageLink2: number = 0

  build() {
    Column({ space: 20 }) {
      Text('子组件的值是:' + this.storageLink2)
      Button() {
        Text('点我加一哦~~')
      }
      .onClick(() => {
        this.storageLink2 += 1
      })
    }
    .backgroundColor(Color.Pink)

  }
}

在这个demo里面无论是点击父组件的按钮,还是点击子组件的按钮,存储起来的数据都是会发生改变的,然后在另外一个组件里面也是会同步发生改变的,因此他的数据传递方向就是双向的,和@LocalStorageProp比起来,数据的更改又方便一点。但是它还是存在一个致命性的问题,就是当我们每次运行代码的时候,它的数据都是会回到最开始设定的那一个,在项目里面这当然是不可取的,因此就有了另外的一个替代方案AppStorage。

AppStorage

@StorageProp

使用之前我们首先需要先将相关的数据存储起来,例如我们在实现沉浸式现实的时候,需要获取到当前屏幕的安全区高度,我们将其存储起来,然后在后续需要使用的地方使用,因此就需要先使用到一个api,AppStorage.setOrCreate(key,value),

import { Key } from '../constant/key';


@Entry
@Component
struct Parent {
  @StorageProp(Key.topHEIGHT) topHEIGHT: number = 0

  build() {
    Column({ space: 20 }) {
      Button(`点我加一哦~~`)
        .onClick(() => {
          this.topHEIGHT += 1;
        })
      Text('父组件的值是:' + this.topHEIGHT)
      Child()
    }
    .padding({
      top: 40,
      left: 20,
      right: 20
    })
  }
}


@Component
struct Child {
  @StorageProp(Key.topHEIGHT) topHeight: number = 0

  build() {
    Column({ space: 20 }) {
      Text('子组件的值是:' + this.topHeight)
      Button() {
        Text('点我加一哦~~')
      }
      .onClick(() => {
        this.topHeight += 1
      })
    }
    .backgroundColor(Color.Pink)

  }
}

在这个例子里面,我们通过使用@StorageProp,利用唯一的key,来获取相关的数据,就可以保证数据的唯一性,并且不会随着项目的运行而发生改变。但是还是存在一个问题,就是这个数据就是我们第一次存起来的值,如果后面数据的值发生了改变,这个数据是无法发生改变的,因此就需要使用到另外的一个方法了@StorageLink(key,value)。

@StorageLink

import { Key } from '../constant/key';

@Entry
@Component
struct Parent {
  @StorageLink(Key.TestLink) testLink: number = 0

  aboutToAppear(): void {
    console.log('aboutToAppear', this.testLink)
  }

  build() {
    Column({ space: 20 }) {
      Text('没有发生更改的值是:' + this.testLink)
      Child()
    }
    .padding({
      top: 40,
      left: 20,
      right: 20
    })
  }
}


@Component
struct Child {
  @StorageLink(Key.TestLink) testLink: number = 0
  build() {
    Column({ space: 20 }) {
      Text('发生更改后的值是:' + this.testLink)
        .onClick(() => {
          this.testLink += 1
        })
    }
    .backgroundColor(Color.Pink)

  }
}

以上就是AppStorage的两种最常用的两种方法,还有很多其他的方法需要我们自己在使用的时候自己去慢慢探索。

Preferences

PersistentStorage是应用程序中的可选单例对象,使用这个之前我们需要依赖AppStorage提供的持久化和读回UI的能力。

保存简单数据类型,例如number, string, boolean, enum

PersistentStorage.persistProp<string>('info','感觉自己闷闷哒')

@Entry
@Component
struct PersistentStoragePage01 {
  @StorageLink('info')
  info:string=''

  build() {
    Row() {
      Column() {
        Text(this.info)
          .fontSize(50)
          .fontWeight(FontWeight.Bold)
          .onClick(()=>{
            this.info+='!'
          })
      }
      .width('100%')
    }
    .height('100%')
  }
}

保存复杂数据类型,我们以一个自定义的对象来举例

interface FoodInfo {
  name: string
  price: number
}

PersistentStorage.persistProp<FoodInfo[]>('foods', [
  { name: '西兰花炒蛋', price: 10 },
  { name: '猪脚饭', price: 15 },
  { name: '剁椒鱼头', price: 14 },
])

@Entry
@Component
struct PersistentStoragePage02 {
  @StorageLink('foods')
  foods: FoodInfo[] = []

  build() {
    Row() {
      Column() {
        Text(JSON.stringify(this.foods))
          .fontSize(20)
          .fontWeight(FontWeight.Bold)
          .onClick(() => {
            //this.foods[0].name='花菜炒蛋'
            this.foods[0] = {
              name: '花草炒蛋',
              price: 29
            }
          })
      }
      .width('100%')
    }
    .height('100%')
  }
}

以上就是三种不同数据存储的方法,每一个方法都只有自己的优劣点,我们在日常的开发中需要通过自己的使用熟练度和具体情况来高效的选择它们,这样才能发挥出最有效的作用。

Logo

中国智能体开发者社区,聚焦智能体与大模型开发,提供前沿资讯、实用工具链、开源项目及行业案例。通过技术沙龙、开发者大赛等活动,促进经验交流与协作,助力开发者快速构建创新智能应用。

更多推荐