HarmonyOS-基礎之狀態資料共享

我也有梦想呀發表於2024-04-19

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 })
    }
  }
}

image

從上面的例子可以看出:

  • 我們在父元件定義的 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 時,不僅父元件當前頁面資料會發生改變,子元件也會發生改變,也就是資料“雙向流動”

image

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%')
  }
}

這玩意兒得在真機上測試

能看到這兒,說明你很不錯了,繼續加油!朋友,未來無限可能

相關文章