鸿蒙开发-ArkTS 语言-状态管理

news2024/11/16 18:07:05

鸿蒙开发-ArkTS 语言-基础语法

3. 状态管理

变量必须被装饰器装饰才能成为状态变量,状态变量的改变才能导致 UI 界面重新渲染

概念描述
状态变量被状态装饰器装饰的变量,改变会引起UI的渲染更新。
常规变量没有状态的变量,通常应用于辅助计算。它的改变永远不会引起UI的刷新。
数据源/同步源状态变量的原始来源,可以同步给不同的状态数据。通常意义为父组件传给子组件的数据。
命名参数机制父组件通过指定参数传递给子组件的状态变量,为父子传递同步参数的主要手段。示例:CompA: ({ aProp: this.aProp })。
从父组件初始化父组件使用命名参数机制,将指定参数传递给子组件。本地初始化的默认值在有父组件传值的情况下,会被覆盖。

Components部分的装饰器为组件级别的状态管理,Application部分为应用的状态管理。可以通@StorageLink/@LocalStorageLink和@StorageProp/@LocalStorageProp实现应用和组件状态的双向和单向同步。图中箭头方向为数据同步方向,单箭头为单向同步,双箭头为双向同步。

图片来源:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V2/arkts-state-management-overview-0000001524537145-V2

img03

状态管理分三种:

  1. 组件级别,即components级别:

    • @State:@State装饰的变量拥有其所属组件的状态,可以作为其子组件单向和双向同步的数据源。当其数值改变时,会引起相关组件的渲染刷新。
    • @Prop:@Prop装饰的变量可以和父组件建立单向同步关系,@Prop装饰的变量是可变的,但修改不会同步回父组件。
    • @Link:@Link装饰的变量和父组件构建双向同步关系的状态变量,父组件会接受来自@Link装饰的变量的修改的同步,父组件的更新也会同步给@Link装饰的变量。
    • @Provide/@Consume:@Provide/@Consume装饰的变量用于跨组件层级(多层组件)同步状态变量,可以不需要通过参数命名机制传递,通过alias(别名)或者属性名绑定。
    • @Observed:@Observed装饰class,需要观察多层嵌套场景的class需要被@Observed装饰。单独使用@Observed没有任何作用,需要和@ObjectLink、@Prop连用。
    • @ObjectLink:@ObjectLink装饰的变量接收@Observed装饰的class的实例,应用于观察多层嵌套场景,和父组件的数据源构建双向同步。
  2. 应用级别,即 Application 级别:

    • AppStorage是应用程序中的一个特殊的单例LocalStorage对象,是应用级的数据库,和进程绑定,通过@StorageProp和@StorageLink装饰器可以和组件联动。
    • AppStorage是应用状态的“中枢”,需要和组件(UI)交互的数据存入AppStorage,比如持久化数据PersistentStorage和环境变量Environment。UI再通过AppStorage提供的装饰器或者API接口,访问这些数据;
    • 框架还提供了LocalStorage,AppStorage是LocalStorage特殊的单例。LocalStorage是应用程序声明的应用状态的内存“数据库”,通常用于页面级的状态共享,通过@LocalStorageProp和@LocalStorageLink装饰器可以和UI联动。
  3. 其他状态管理功能

    • @Watch用于监听状态变量的变化。

    • $$ 运算符:给内置组件提供TS变量的引用,使得TS变量和内置组件的内部状态保持同步。

3.1 组件级别的状态管理

3.1.1 @State 组件内状态

@State装饰的变量,是私有的,只能从组件内部访问,在声明时必须指定其类型和本地初始化。初始化也可选择使用命名参数机制从父组件完成初始化。

示例如下:

@Entry
@Component
struct Index {
  build() {
    Column() {
      Parent()
    }
  }
}

@Component
struct MyComponent {
  @State count: number = 0;
  private increaseBy: number = 1;

  build() {
    Column(){
      Row() {
        Text(`${this.count}`)
          .fontSize(108)
      }
      Row() {
        Button('click me hh').onClick(() => {
          this.count += this.increaseBy
        })
      }
    }
  }
}

@Component
struct Parent {
  build() {
    Column() {
      // 从父组件初始化,覆盖本地定义的默认值
      MyComponent({ count: 2, increaseBy: 2 })
    }
  }
}

效果如下:

gif06

3.1.2 能观察的数据类型

  1. 观察简单类型的变化:
    • 当状态变量是boolean、string、number类型时,修改这些变量的数值可以被观察到,从而引起UI的刷新。
@State count: number = 0;
this.count = 1; // 这种修改可以被观察到
  1. 观察类和对象类型的变化:
    • 对于class或Object类型,可以观察到自身的赋值变化以及其属性的赋值变化。但是嵌套属性的赋值观察不到。
class ClassA {
  public value: string;

  constructor(value: string) {
    this.value = value;
  }
}

class Model {
  public value: string;
  public name: ClassA;
  constructor(value: string, a: ClassA) {
    this.value = value;
    this.name = a;
  }
}

@State title: Model = new Model('Hello', new ClassA('World'));

// class类型赋值可以观察到
this.title = new Model('Hi', new ClassA('ArkUI'));

// class属性的赋值可以观察到
this.title.value = 'Hi';

// 嵌套属性的赋值观察不到
this.title.name.value = 'ArkUI';
  1. 观察数组类型的变化:
    • 当状态变量是数组时,可以观察到数组本身的赋值以及对数组的添加、删除、更新操作。
class Model {
  public value: number;
  constructor(value: number) {
    this.value = value;
  }
}

@State title: Model[] = [new Model(11), new Model(1)];

// 数组自身的赋值可以观察到
this.title = [new Model(2)];

// 数组项的赋值可以观察到
this.title[0] = new Model(2);

// 删除数组项可以观察到
this.title.pop();

// 新增数组项可以观察到
this.title.push(new Model(12));
3.1.2 @Prop装饰器:父子单向通信

可以使用 @Prop 定义要从父级接受的变量。注意以下两点:

  1. @Prop变量可以在子组件内修改,但修改后的变化不会同步回父组件中。

  2. 但当父组件中的数据源更改时,与之相关的@Prop装饰的变量都会自动更新。如果子组件已经在本地修改了@Prop装饰的相关变量值,而在父组件中对应的@State装饰的变量被修改后,子组件本地修改的@Prop装饰的相关变量值将被覆盖。

  3. 允许装饰的变量的类型:string、number、boolean、enum类型。

  4. 不允许的类型:any,undefined和null。

@Prop 初始化规则:

图示:

图片来源:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V2/arkts-prop-0000001473537702-V2

img04

代码示例:

@Component
struct CountDownComponent {
  @Prop count: number;
  costOfOneAttempt: number = 1;

  build() {
    Column() {
      if (this.count > 0) {
        Text(`儿子还有 ${this.count} 个萝卜头`)
      } else {
        Text('吃完了,要挨打了')
      }
      // @Prop装饰的变量不会同步给父组件
      Button(`儿子偷吃了${this.costOfOneAttempt}个萝卜头`).onClick(() => {
        this.count -= this.costOfOneAttempt;
      })
    }
  }
}

@Entry
@Component
struct ParentComponent {
  @State countDownStartValue: number = 10;

  build() {
    Column() {
      Text(`老爸还有 ${this.countDownStartValue} 个萝卜头`)
      // 父组件的数据源的修改会同步给子组件
      Button(`一起买了1个萝卜头`).onClick(() => {
        this.countDownStartValue += 1;
      })
      // 父组件的修改会同步给子组件
      Button(`一起吃了1个萝卜头`).onClick(() => {
        this.countDownStartValue -= 1;
      })

      CountDownComponent({ count: this.countDownStartValue, costOfOneAttempt: 2 })
    }
  }
}

效果如下:

gif07

3.1.3 @Link: 父子双向通信

可以使用 @Link 装饰器进行数据双向同步:

  • @Link 是双向同步的装饰器,父组件中@State, @StorageLink和@Link 和子组件@Link可以建立双向数据同步,反过来也是可以的。
  • 允许装饰的类型有:Object、class、string、number、boolean、enum类型,同时类型必须被指定,且和双向绑定状态变量的类型相同。不支持any,不支持简单类型和复杂类型的联合类型,不允许使用undefined和null。
  • 当装饰的数据类型为boolean、string、number时,可以同步观察到数值的变化;为class或Object时,可以观察到赋值和属性赋值的变化;为array时,可以观察到数组添加、删除、更新数组单元的变化。

@Link初识化规则:

图示:

图片来源:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V2/arkts-link-0000001524297305-V2

img05

代码示例:

class GreenButtonState {
  width: number = 0;

  constructor(width: number) {
    this.width = width;
  }
}

// class类型的子组件GreenButton,通过@Link装饰变量greenButtonState同步到父组件Index中的@State变量greenButtonState

@Component
struct GreenButton {
  @Link greenButtonState: GreenButtonState;

  build() {
    Button('绿色按钮')
      .width(this.greenButtonState.width)
      .height(150.0)
      .fontWeight(FontWeight.Bold)
      .backgroundColor('#ff81c123')
      .onClick(() => {
        if (this.greenButtonState.width < 700) {
          // 更新class的属性,变化可以被观察到同步回父组件
          this.greenButtonState.width += 125;
        } else {
          // 更新class,变化可以被观察到同步回父组件
          this.greenButtonState = new GreenButtonState(100);
        }
      })
  }
}

// 简单类型的子组件YellowButton,通过@Link装饰变量yellowButtonState同步到父组件Index中的@State变量yellowButtonProp
@Component
struct YellowButton {
  @Link yellowButtonState: number;

  build() {
    Button('黄色按钮')
      .width(this.yellowButtonState)
      .height(150.0)
      .fontWeight(FontWeight.Bold)
      .backgroundColor('#ffdb944d')
      .onClick(() => {
        // 子组件的简单类型可以同步回父组件
        this.yellowButtonState += 50.0;
      })
  }
}

// 父组件Index,初始化并管理子组件的状态变量
@Entry
@Component
struct Index {
  @State greenButtonState: GreenButtonState = new GreenButtonState(300);
  @State yellowButtonProp: number = 100;

  build() {
    Column() {
      // 按钮:父组件修改greenButtonState,同步到GreenButton子组件
      Button('父视图: 设置 绿色按钮')
        .onClick(() => {
          this.greenButtonState.width = (this.greenButtonState.width < 700) ? this.greenButtonState.width + 100 : 100;
        })
      // 按钮:父组件修改yellowButtonProp,同步到YellowButton子组件
      Button('父视图: 设置 黄色按钮')
        .onClick(() => {
          this.yellowButtonProp = (this.yellowButtonProp < 700) ? this.yellowButtonProp + 100 : 100;
        })

      // 子组件:GreenButton,通过@Link同步更新父组件@State的greenButtonState
      GreenButton({ greenButtonState: $greenButtonState })

      // 子组件:YellowButton,通过@Link同步更新父组件@State的yellowButtonProp
      YellowButton({ yellowButtonState: $yellowButtonProp })
    }
  }
}

图示:

gif08

3.1.4 @Provide/@Consume 装饰器

为了避免在组件中多次传递变量,推出了一种使某些变量能被所有后代组件使用的装饰器 @Provide,后代组件可以使用 @Consume去获取 @Provide 的值,而@State和@Link 只能在父子组件中传递。

// 通过相同的变量名绑定
@Provide a: number = 0;
@Consume a: number;

// 通过相同的变量别名绑定
@Provide('a') b: number = 0;
@Consume('a') c: number;

@Provide 和 @Consume 是双向同步的。

代码示例:

@Component
struct CompD {
  // @Consume装饰的变量通过相同的属性名绑定其祖先组件CompA内的@Provide装饰的变量
  @Consume tickets: number;

  build() {
    Column() {
      Text(`评审投票数(${this.tickets})`)
      Button(`评审投票数(${this.tickets}),+1`)
        .onClick(() => this.tickets += 1)
    }
    .width('50%')
  }
}

@Component
struct CompC {
  build() {
    Row({ space: 5 }) {
      CompD()
      CompD()
    }
  }
}

@Component
struct CompB {
  build() {
    CompC()
  }
}

@Entry
@Component
struct Index {
  // @Provide装饰的变量reviewVotes由入口组件CompA提供其后代组件
  @Provide tickets: number = 0;

  build() {
    Column() {
      Button(`评审投票数(${this.tickets}),+1`)
        .onClick(() => this.tickets += 1)
      CompB()
    }
  }
}

图示:

img06

3.1.5 @Observed 装饰器和@ObjectLink装饰器

对于多层嵌套的情况,比如二维数组,或者数组项class,或者class的属性是class,他们的第二层的属性变化是无法观察到的。这就引出了@Observed/@ObjectLink装饰器。

@ObjectLink和@Observed类装饰器用于在涉及嵌套对象或数组的场景中进行双向数据同步:

  • 被@Observed装饰的类,可以被观察到属性的变化;
  • 子组件中@ObjectLink装饰器装饰的状态变量用于接收@Observed装饰的类的实例,和父组件中对应的状态变量建立双向数据绑定。这个实例可以是数组中的被@Observed装饰的项,或者是class object中的属性,这个属性同样也需要被@Observed装饰。
  • 单独使用@Observed是没有任何作用的,需要搭配@ObjectLink或者@Prop使用。
  • 不要用@Observed和其他类装饰器装饰同一个class,@Observed会改变class的原型链

初始化图示:

图片来源:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V2/arkts-observed-and-objectlink-0000001473697338-V2#section2976114355019

img07

以二维数组监听为例:

@Observed
class StringArray extends Array<String> {
}

@Component
struct ItemPage {
  @ObjectLink itemArr: StringArray;

  build() {
    Row() {
      Text('ItemPage')
        .width(100).height(100)

      ForEach(this.itemArr,
        item => {
          Text(item)
            .width(100).height(100)
        },
        item => item
      )
    }
  }
}

@Entry
@Component
struct Index {
  @State arr: Array<StringArray> = [new StringArray(), new StringArray(), new StringArray()];

  build() {
    Column() {
      ItemPage({ itemArr: this.arr[0] })
      ItemPage({ itemArr: this.arr[1] })
      ItemPage({ itemArr: this.arr[2] })

      Divider()

      ForEach(this.arr,
        itemArr => {
          ItemPage({ itemArr: itemArr })
        },
        itemArr => itemArr[0]
      )

      Divider()

      Button('update')
        .onClick(() => {
          console.error('Update all items in arr');
          if (this.arr[0][0] !== undefined) {
            // 正常情况下需要有一个真实的ID来与ForEach一起使用,但此处没有
            // 因此需要确保推送的字符串是唯一的。
            this.arr[0].push(`${this.arr[0].slice(-1).pop()}${this.arr[0].slice(-1).pop()}`);
            this.arr[1].push(`${this.arr[1].slice(-1).pop()}${this.arr[1].slice(-1).pop()}`);
            this.arr[2].push(`${this.arr[2].slice(-1).pop()}${this.arr[2].slice(-1).pop()}`);
          } else {
            this.arr[0].push('Hello');
            this.arr[1].push('World');
            this.arr[2].push('!');
          }
        })
    }
  }
}

效果如下:

gif09

3.2 App级别的状态管理

ArkTS可以实现多种应用状态管理的能力,具体表现在以下几点:

  • LocalStorage: 是页面级的状态储存,通常用于 UIAblility内,也可以用于页面间状态共享
  • AppStorage: 特殊的单例 LocalStorage 对象,为程序的 UI 状态属性提供中央储存
  • PersistentStorage: 持久化 UI 状态储存,通常于 AppStorage 一起使用,可以把 AppStorage 存储的数据写入磁盘,确保数据在重启前后保持一致
  • Environment: 应用程序运行的设备环境参数,会同步到 AppStorage 中,可以和AppStorage 搭配使用
3.2.1 LocalStorage: 页面级 UI 状态存储

有关 LocalStorage 介绍:

  • 一个应用程序可以创建多个 LocalStorage 实例,这些实例可以在页面内共享,也可以通过 GetShared 接口在 UIAbility 中创建的实例实现跨页面和 UIAbility 内的共享。
  • 根据组件树的结构,被 @Entry 装饰的 @Component 实例可以被分配一个 LocalStorage 实例,而该组件的所有子组件实例将自动获得对该 LocalStorage 实例的访问权限。
  • @Component 装饰的组件最多可以访问一个 LocalStorage 实例和 AppStorage,而未被 @Entry 装饰的组件无法独立分配 LocalStorage 实例,只能接受父组件通过 @Entry 传递来的 LocalStorage 实例。
  • 一个 LocalStorage 实例在组件树上可以被分配给多个组件,而其中的所有属性都是可变的。
  • 当应用释放最后一个指向LocalStorage的引用时,比如销毁最后一个自定义组件,LocalStorage将被JS Engine垃圾回收。
  • 被绑定的属性值变化(无论父子级关系如何),都会引起依赖此变量的 UI 刷新渲染。

装饰器

  • @LocalStorageProp: @LocalStorageProp(key) 是一个装饰器,用于在 ArkUI 组件框架中建立自定义组件的属性与 LocalStorage 中特定键对应属性之间的单向数据同步。
    • 初始化与绑定: 在自定义组件初始化时,被 @LocalStorageProp(key) 装饰的变量通过给定的 key 绑定到相应的 LocalStorage 属性上,完成初始化。本地初始化是必要的,因为不能保证在组件初始化之前 LocalStorage 中是否存在给定的 key

    • 本地修改和同步: 对于使用 @LocalStorageProp(key) 装饰的变量,本地的修改是允许的。但是需要注意,本地的修改永远不会同步回 LocalStorage 中。相反,如果 LocalStorage 中给定 key 的属性发生改变,这个改变会被同步给被 @LocalStorageProp(key) 装饰的变量,并覆盖本地的修改。

  • @LocalStorageLink@LocalStorageLink 是一个装饰器,用于在 ArkUI 组件框架中建立自定义组件的状态变量与 LocalStorage 中特定键对应属性之间的双向数据同步。以下是相关信息的概述:
    • 同步规则: @LocalStorageLink(key)LocalStorage 中给定 key 对应的属性建立双向数据同步。这包括本地的修改会同步回 LocalStorage 中,以及 LocalStorage 中的修改会被同步到所有绑定了相同 key 的属性上。
    • 初始化规则@LocalStorageLink 不支持从父节点初始化,只能从 LocalStorage 中的 key 对应的属性初始化。如果没有对应的 key,则使用本地默认值初始化。可用于初始化 @State@Link@Prop@Provide,但不支持组件外访问

语法示例:

let storage = new LocalStorage({ 'KeyA': 47 }); // 创建新实例并使用给定对象初始化
let keyA = storage.get('KeyA'); // keyA == 47

let link1 = storage.link('KeyA'); // link1.get() == 47
let link2 = storage.link('KeyA'); // link2.get() == 47

let prop = storage.prop('KeyA'); // prop.get() == 47

link1.set(48); // 双向绑定 link1.get() == link2.get() == prop.get() == 48

prop.set(1); // 单向绑定 prop.get() == 1;但是 link1.get() == link2.get() == 48

link1.set(49); // 双向绑定 link1.get() == link2.get() == prop.get() == 49

案例理解:

// 创建新实例并使用给定对象初始化
let localStorageInstance = new LocalStorage({ 'PropertyA': 47 });

@Component
struct ChildComponent {
  // 与LocalStorage中的'PropertyA'属性建立双向绑定
  @LocalStorageLink('PropertyA') linkedProperty2: number = 1;
  // 与LocalStorage中的'PropertyA'属性建立单向绑定
  @LocalStorageProp('PropertyA') linkedProperty3: number = 1;

  build() {
    Row() {
      Button(`子组件的值 ${this.linkedProperty2}`)
        // 更改将同步至LocalStorage中的'PropertyA'以及ParentComponent.linkedProperty1
        .onClick(() => this.linkedProperty2 += 1)
      // Local 中值变化,linkedProperty3 也会变,反之不会
      Button(`子组件的值-单向 Prop ${this.linkedProperty3}`)
        .onClick(() => this.linkedProperty3 += 1)
    }
  }
}

// 使LocalStorage可被@Component组件访问
@Entry(localStorageInstance)
@Component
struct ParentComponent {
  // @LocalStorageLink变量装饰器与LocalStorage中的'PropertyA'属性建立双向绑定
  @LocalStorageLink('PropertyA') linkedProperty1: number = 1;

  build() {
    Column({ space: 15 }) {
      Button(`父组件的值 ${this.linkedProperty1}`) // 初始化值从LocalStorage中获取,因为'PropertyA'已经初始化为47
        .onClick(() => this.linkedProperty1 += 1)
      // @Component子组件自动获得对ParentComponent LocalStorage实例的访问权限。
      ChildComponent()
    }
  }
}

效果如下:

gif10

3.2.2 AppStorage:应用全局的UI状态存储

概述

AppStorage是应用全局的UI状态存储,是和应用的进程绑定的,由UI框架在应用程序启动时创建,为应用程序UI状态属性提供中央存储。AppStorage是应用级的全局状态共享,相当于整个应用的“中枢”,持久化数据PersistentStorage和 环境变量Environment都是通过AppStorage的中转,才可以和UI交互。AppStorage中的属性可以被双向同步,数据可以是存在于本地或远程设备上,并具有不同的功能,比如数据持久化。

基本用法:

// 在AppStorage中设置或创建 'PropA' 属性,初始值为 47
AppStorage.SetOrCreate('PropA', 47);

let localStorageInstance: LocalStorage = new LocalStorage({ 'PropA': 17 });

// 从AppStorage中获取 'PropA' 的值,此时 propA 在 AppStorage 中为 47,在 LocalStorage 中为 17
let propAFromAppStorage: number = AppStorage.Get('PropA');

// 使用AppStorage的Link方法创建两个链接(link1和link2),以同步 'PropA' 的值
let link1: SubscribedAbstractProperty<number> = AppStorage.Link('PropA');
let link2: SubscribedAbstractProperty<number> = AppStorage.Link('PropA');

// 使用AppStorage的Prop方法创建一个属性(prop),以单向同步 'PropA' 的值
let prop: SubscribedAbstractProperty<number> = AppStorage.Prop('PropA');

// 修改 link1,演示双向绑定的效果,所有其他绑定到相同键的变量都同步更新
link1.set(48); // link1.get() == link2.get() == prop.get() == 48

// 修改属性 prop,演示单向绑定的效果,只有属性本身更新,其他变量不受影响
prop.set(1); // prop.get() == 1;但是 link1.get() == link2.get() == 48

// 再次修改 link1,验证双向绑定,所有绑定到相同键的变量都同步更新
link1.set(49); // link1.get() == link2.get() == prop.get() == 49

// 使用 'PropA' 的值从 LocalStorage 中获取,此时为 17 
let valueFromLocalStorage: number = localStorageInstance.get('PropA');

// 设置 'PropA' 的值为 101
localStorageInstance.set('PropA', 101);

// 从 LocalStorage 中获取 'PropA' 的值,此时为 101
let valueFromLocalStorageAfterSet: number = localStorageInstance.get('PropA');

// 从 AppStorage 中获取 'PropA' 的值,此时为 49
let valueFromAppStorage: number = AppStorage.Get('PropA');

// 获取 link1 的值,此时为 49
let valueFromLink1: number = link1.get();

// 获取 link2 的值,此时为 49
let valueFromLink2: number = link2.get();

// 获取 prop 的值,此时为 49
let valueFromProp: number = prop.get();

3.2.3 PersistentStorage:持久化存储UI状态

LocalStorage和AppStorage都是运行时的内存,但是在应用退出再次启动后,依然能保存选定的结果,是应用开发中十分常见的现象,这就需要用到PersistentStorage。

注意:

  • 持久化数据操作相对较慢,应避免持久化大型数据集和经常变化的变量。
  • PersistentStorage的持久化变量最好是小于2kb的数据,以避免影响UI渲染性能。
  • PersistentStorage只能在UI页面内使用,否则无法持久化数据。
  • 不在调用 PersistentStorage 前调用 AppStorage,会导致上次退出应用保存的值丢失。

代码示例:

PersistentStorage.PersistProp('userScore', 100);

@Entry
@Component
struct Game {
  @State message: string = 'Welcome to the Game'
  @StorageLink('userScore') userScore: number = 50

  build() {
    Row() {
      Column() {
        Text(this.message)
        Text(`你的得分: ${this.userScore}`)
          .onClick(() => {
            this.userScore += 10;
          })
      }
    }
  }
}
3.2.4 Environment:设备环境查询

Environment设备环境查询用于查询设备运行环境参数,是ArkUI框架在应用程序启动时创建的单例对象,它为AppStorage提供了一系列描述应用程序运行状态的属性。Environment的所有属性都是不可变的(即应用不可写入),所有的属性都是简单类型。

使用场景

Environment.EnvProp将设备的语言设置为英语 'zh'。然后,@StorageProp将设备语言与AppStorage中的 deviceLanguage 建立了单向同步。

// 将设备的语言code存入AppStorage
Environment.EnvProp('deviceLanguage', 'zh');

@Entry
@Component

struct Main {
  // 使用@StorageProp链接到Component中
  @StorageProp('deviceLanguage') selectedLanguage: string = 'en';

  build() {
    Row() {
      Column() {
        Text('Device Language:')
        Text(this.selectedLanguage)
      }
    }
  }
}

// 应用逻辑使用Environment
// 从AppStorage获取单向绑定的languageCode的变量
const lang: SubscribedAbstractProperty<string> = AppStorage.Prop('deviceLanguage');
if (lang.get() === 'zh') {
  console.info('你好');
} else {
  console.info('Hello!');
}

3.3 其他状态管理

3.3.1 @Watch 监听

@Watch用于监听状态变量的变化,当状态变量变化时,@Watch的回调方法将被调用。@Watch在ArkUI框架内部判断数值有无更新使用的是严格相等(===),遵循严格相等规范。当在严格相等为false的情况下,就会触发@Watch的回调。

注意:

  1. 当观察到状态变量的变化(包括双向绑定的AppStorage和LocalStorage中对应的key发生的变化)的时候,对应的@Watch的回调方法将被触发;
  2. 如果在@Watch的方法里改变了其他的状态变量,也会引起状态变更和@Watch的执行,要避免无限循环
  3. 在第一次初始化的时候,@Watch装饰的方法不会被调用,即认为初始化不是状态变量的改变。

示例:

@Component
struct TotalView {
  @Prop @Watch('onCountUpdated') count: number;
  @State total: number = 0;
  // @Watch cb
  onCountUpdated(propName: string): void {
    this.total += this.count;
  }

  build() {
    Text(`Total: ${this.total}`)
  }
}

@Entry
@Component
struct CountModifier {
  @State count: number = 0;

  build() {
    Column() {
      Button('add to basket')
        .onClick(() => {
          this.count++
        })
      TotalView({ count: this.count })
    }
  }
}
3.3.2 $$语法:内置组件双向同步

$$运算符为系统内置组件提供TS变量的引用,使得TS变量和系统内置组件的内部状态保持同步。

内部状态具体指什么取决于组件。

  • 当前$$支持基础类型变量,以及@State、@Link和@Prop装饰的变量。
  • 当前$$仅支持 bindPopup 属性方法的show参数,Radio组件的checked属性,Refresh组件的refreshing参数。
  • $$绑定的变量变化时,会触发UI的同步刷新。
@Entry
@Component
struct bindPopupPage {
  @State customPopup: boolean = false;

  build() {
    Column() {
      Button('Popup')
        .margin(20)
        .onClick(() => {
          this.customPopup = !this.customPopup
        })
        .bindPopup($$this.customPopup, {
          message: 'showPopup'
        })
    }
  }
}

鸿蒙开发-ArkTS 语言-循环渲染

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1250705.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

1. git入门操作

1. git入门操作 1、基本名词解释 图片 名词含义index索引区&#xff0c;暂存区master分支名&#xff0c;每个仓库都有个master&#xff0c;它作为主分支。branch其他分支&#xff0c;我们可以把master分支上的代码拷贝一份&#xff0c;重新命名为其他分支名work space就是我…

竞赛python区块链实现 - proof of work工作量证明共识算法

文章目录 0 前言1 区块链基础1.1 比特币内部结构1.2 实现的区块链数据结构1.3 注意点1.4 区块链的核心-工作量证明算法1.4.1 拜占庭将军问题1.4.2 解决办法1.4.3 代码实现 2 快速实现一个区块链2.1 什么是区块链2.2 一个完整的快包含什么2.3 什么是挖矿2.4 工作量证明算法&…

PyTorch-ReID重识别算法库与数据集资料汇总

Torchreid 是一个用于深度学习人员重新识别的库&#xff0c;用 PyTorch 编写&#xff0c;为我们的 ICCV’19 项目 Omni-Scale Feature Learning for Person Re-Identification 开发。 PyTorch-ReID的特点是 多GPU训练支持图像和视频 REID端到端培训和评估极其轻松地准备 Rei…

如何找出excel中两列数据中不同的值(IF函数的用法)

第一部分&#xff0c;举例&#xff1a; 例1&#xff1a; 如下图所示&#xff0c;A列和B列是需要比较的数据&#xff0c;C列为对比规则&#xff1a;IF(A2B2,"是","否") 示例图 例2&#xff1a;给B列的成绩评等级 C列的规则&#xff1a; IF(B2>85,&qu…

RPC和HTTP的区别

目录 1、RPC是什么 1.1 概念 1.2 RPC的组成部分 1.3 常见的 RPC 技术和框架 1.4 RPC的工作流程 2、HTTP是什么 2.1 概念 2.2 HTTP的消息格式 2.3 HTTP响应状态码有哪些 3、⭐RPC和HTTP的区别 小结 1、RPC是什么 1.1 概念 RPC&#xff08;Remote Procedure Call&am…

全国甲骨文识读大会用到哪些竞赛软件

2023年&#xff0c;全国甲骨文识读大会第1季在“中华字都安阳”举办&#xff0c;天纵竞赛软件为此次高端知识竞赛提供软件支持。 甲骨文识读大会分为海选、初赛、复赛、决赛4个阶段&#xff0c;不分年龄、性别、职业、地域&#xff0c;并邀请国内甲骨文和殷商文化方面专家学者组…

Unity-类-Vector

Vector矢量 是一个基本的数学概念,它允许你描述方向和大小。在游戏和应用中,矢量通常用于描述一些基本属性,如角色的位置、物体移动的速度或两个物体之间的距离。 矢量算术是计算机编程很多方面(如图形、物理和动画)的基础,深入了解这一主题对于充分发挥 Unity 的功能很有…

python实现炫酷的屏幕保护程序!

今天写了桌面保护程序。先来看看效果吧。 完全可以作为屏保程序了&#xff0c;老方式&#xff1a;以下是实现的代码&#xff1a; from tkinter import *from time import strftime​def update_time():global i, j current_time strftime(%H:%M:%S)time_label.config(textcu…

软件工程简明教程

软件工程简明教程 何为软件工程&#xff1f; 1968 年 NATO&#xff08;北大西洋公约组织&#xff09;提出了软件危机&#xff08;Software crisis&#xff09;一词。同年&#xff0c;为了解决软件危机问题&#xff0c;“软件工程”的概念诞生了。一门叫做软件工程的学科也就应…

前车之鉴: 适用于所有select选择框的 全选反选逻辑,如何只用单个change事件优雅完成

文章目录 实际效果1.1 效果展示1.2 核心功能 Show CodeQ & A彩蛋 实际效果 1.1 效果展示 1.2 核心功能 区别网上其他思路&#xff0c;我这里不需要使用原生点击事件&#xff0c;将全选反选逻辑收敛在一个change事件上 此前已经看过一些全选逻辑同学尝试过后&#xff0c;会…

NX二次开发UF_CURVE_ask_combine_curves 函数介绍

文章作者&#xff1a;里海 来源网站&#xff1a;https://blog.csdn.net/WangPaiFeiXingYuan UF_CURVE_ask_combine_curves Defined in: uf_curve.h int UF_CURVE_ask_combine_curves(tag_t combine_curve_feature, tag_t * first_curve_tag, UF_CURVE_combine_curves_directi…

【阿里云】图像识别 智能分类识别 增加垃圾桶开关盖功能点和OLED显示功能点(二)

一、增加垃圾桶开关盖功能 环境准备 二、PWM 频率的公式 三、pthread_detach分离线程&#xff0c;使其在退出时能够自动释放资源 四、具体代码实现 图像识别数据及调试信息wget-log打印日志文件 五、增加OLED显示功能 六、功能点实现语音交互视频 一、增加垃圾桶开关盖功能…

vue3(一)-基础入门

一、导入vue.js 1.可以借助 script 标签直接通过 CDN 来使用 Vue <!-- <script src"https://unpkg.com/vue3/dist/vue.global.js"></script> -->2.也可以下载vue.global.js文件并在本地导入 <script src"./lib/vue.global.js">&…

3、Qt使用windeploy工具打包可执行文件

新建一个文件夹&#xff0c;把要打包的可执行文件exe拷贝过来 点击输入框&#xff0c;复制一下文件夹路径 点击电脑左下角&#xff0c;找到Qt文件夹&#xff0c; 点击打开 “Qt 5.12.0 for Desktop” &#xff08;我安装的是Qt 5.12.0版本&#xff09; 输入“cd bin”&#xff…

转录组学习第5弹-比对参考基因组

比对参考基因组 在构建文库的过程中需要将DNA片段化&#xff0c;因此测序得到的序列只是基因组的部分序列。为了确定测序reads在基因组上的位置&#xff0c;需要将reads比对回参考基因组上&#xff0c;这个步骤叫做比对&#xff0c;即文献中所提到的alignment或mapping。包括基…

代码随想录算法训练营第30天|回溯总结 332. 重新安排行程

回溯是递归的副产品&#xff0c;只要有递归就会有回溯&#xff0c;所以回溯法也经常和二叉树遍历&#xff0c;深度优先搜索混在一起&#xff0c;因为这两种方式都是用了递归。 回溯法就是暴力搜索&#xff0c;并不是什么高效的算法&#xff0c;最多再剪枝一下。 回溯算法能解…

自动语音识别 支持86种语言 Dragon Professional 16 Crack

从个体从业者到全球组织&#xff0c;文档密集型行业的专业人士长期以来一直依靠 Dragon 语音识别来更快、更高效地创建高质量文档&#xff0c;减少管理开销&#xff0c;以便他们能够专注于客户。了解 Dragon Professional v16 如何通过单一解决方案提高标准&#xff0c;为各个业…

YB4556 28V、1A、单节、线性锂电池充电IC

YB4556 28V 、 1A 、单节、线性锂电池充电 IC 概述: YB4556H 是一款完整的采用恒定电流 / 恒定电压的高压、大电流、单节锂离子电池线性充电 IC。最高耐压可达 28V&#xff0c;6.5V 自动过压保护&#xff0c;充电电流可达 1A。由于采用了内部 PMOSFET 架构&#xff0c;加上防倒…

推荐6款本周 yyds 的开源项目

&#x1f525;&#x1f525;&#x1f525;本周GitHub项目圈选: 主要包含 链接管理、视频总结、有道音色情感合成、中文文本格式校正、GPT爬虫、深度学习推理 等热点项目。 1、Dub 一个开源的链接管理工具&#xff0c;可自定义域名将繁杂的长链接生成短链接&#xff0c;便于保…

云计算领域的第三代浪潮!

根据IDC不久前公布的数据&#xff0c;2023年上半年中国公有云服务整体市场规模(IaaS/PaaS/SaaS)为190.1亿美元&#xff0c;阿里云IaaS、PaaS市场份额分别为29.9%和27.9%&#xff0c;都远超第二名&#xff0c;是无可置疑的行业领头羊。 随着人工智能&#xff08;AI&#xff09;…