# 14.2:@Local

@Local 装饰器是最基础的状态装饰器,它修饰的变量称为 状态变量,一旦 状态变量 的值发生了变化,那么用到该状态变量的组件会重新渲染从而实现 UI 刷新的功能。

# 14.2.1:约束与限制

  • @Local 装饰器修饰的变量必须 指定类型初始化

    @Local 装饰器修饰的变量必须指定类型和初始化,否则编译报错:Property 'message' has no initializer and is not definitely assigned in the constructor.

# 14.2.2:语法介绍

@Local 的使用很简单,直接在组件内的属性前添加 @Local 装饰器即可,格式为:【@Local attributeName: attributeType = attributeValue】,样例如下:

@Entry @ComponentV2 struct Index {

  @Local userName: string = 'Hello World'; // 在组件属性前添加@Local

  @Local
  bookName : string = "《ArkUI实战》"       // 在组件属性上添加@Local

  build() {}
}
1
2
3
4
5
6
7
8
9

# 14.2.3:简单使用

@Local 装饰器的使用很简单,直接在组件内的变量前添加 @Local 即可。简单样例如下所示:

@Entry @ComponentV2 struct Page_02_state {

  message1: string = 'Hello World' // 变量 message1 没有@Local装饰器

  @Local
  message2: string = 'Hello World' // 变量 message2 添加@Local装饰器

  // @Local message3: string;      // 编译报错,必须对message3赋值

  
  build() {
    Column({space: 10}) {
      Button(this.message1)
        .fontSize(30)
        .id("button1")
        .onClick(() => {
          this.message1 = "Hello, OpenHarmony";
        })
      Button(this.message2)
        .fontSize(30)
        .id("button2")
        .onClick(() => {
          this.message2 = "Hello, 《ArkUI实战》";
        })
    }
    .padding(10)
    .width('100%')
    .height("100%")
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

简单样例运行结果如下所示:

14_2_2_1

上述样例中,属性 message1message2 都赋值了默认值,只是 message2 添加了 @Local 装饰器。分别点击 button1button2 表现如下:

  • 点击button1:

    message1 被赋予了 Hello, OpenHarmony,此时页面无反应;

  • 点击button2:

    message2 被赋予了 Hello, 《ArkUI实战》,此时页面显示新值。

结果按钮2的文字则显示了新值,由此可见是 @Local 装饰器导致了按钮2发生了重绘。

📢:@Local 装饰器修饰的变量必须 指定类型初始化,否则编译报错:Property 'message' has no initializer and is not definitely assigned in the constructor.

# 14.2.4:作用域

@Local 装饰器装饰的变量是私有的,只能在当前组件范围内访问,简单样例如下所示:

@Entry @ComponentV2 struct Page_02_state {

  message1: string = 'Hello World'

  @Local
  message2: string = 'Hello World'

  build() {
    Column({space: 10}) {
      Button(this.message1)
        .fontSize(30)
        .onClick(() => {
          this.message1 = "Hello, 《ArkUI实战》";
        })
      Button(this.message2)
        .fontSize(30)
        .onClick(() => {
          this.message2 = "Hello, 《ArkUI实战》";
        })

      CustomButton()
    }
    .padding(10)
    .width('100%')
    .height("100%")
  }
}

@Component struct CustomButton {

  @Local
  message2: string = 'Hello CustomWidget'

  build() {
    Button(this.message2)
      .fontSize(30)
      .onClick(() => {
        this.message2 = "《ArkUI实战》, Hello";
      })
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

样例运行结果如下图所示:

14_2_3_1

由运行结果可知,父子组件虽然都定义了状态变量,但是 CustomButton 内部的 message2 的值发生了更改但不会引发外部 Page_02_state 组件的状态变化。

# 14.2.5:支持的数据类型

@Local 装饰器目前支持的数据类型主要如下:

  • 简单数据类型

    简单数据类型目前支持的类型有: number, boolean, string, enum。简单样例如下所示:

    enum ColorType {
      R,
      G,
      B
    }
    
    @Entry @ComponentV2 struct Page_02_state {
    
      @Local message1: string  = 'Hello World' // 定义 string  类型
      @Local message2: number  = 10086         // 定义 number  类型
      @Local message3: boolean = false         // 定义 boolean 类型
      @Local message4: ColorType = ColorType.R // 定义  enum   类型
    
      build() {
        Column({space: 10}) {
          Button(this.message1)
            .fontSize(30)
            .onClick(() => {
              this.message1 = "Hello, 《ArkUI实战》";
            })
          Button(this.message2.toString())
            .fontSize(30)
            .onClick(() => {
              this.message2 = 10010
            })
          Button(this.message3.toString())
            .fontSize(30)
            .onClick(() => {
              this.message3 = true
            })
          Button(this.message4.toString())
            .fontSize(30)
            .onClick(() => {
              this.message4 = ColorType.B
            })
        }
        .padding(10)
        .width('100%')
        .height("100%")
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41

    样例运行结果如下图所示:

    14_2_4_1
  • 简单数组类型

    简单数据类型 目前支持的有: number, boolean, string, enum,笔者把由它们组成的 数组 简称为简单数组,目前简单数组支持的操作为:数组重新赋值数组单项赋值数组数据添加数组数据删除,以上支持的操作会引起相关组件的重绘。简单样例如下所示:

    @Entry @ComponentV2 struct Page_02_state {
    
      @Local message2: number[]    = []
    
      build() {
        Column({space: 10}) {
          Text("size: " + this.message2.length + ", total: " + this.total())
            .fontSize(20)
          Button("添加操作")
            .onClick(() => {
              this.message2.push(Math.round(Math.random() * 100)) // 数组添加数据操作
            })
          Button("删除操作")
            .onClick(() => {
              this.message2.pop()                                 // 数组删除数据操作
            })
          Button("重新赋值")
            .onClick(() => {
              this.message2 = [100, 200, 300]                     // 数组重新初始化
            })
    
          Button("单项赋值")
            .onClick(() => {
              this.message2[0] = 500                              // 数组单项数据更新
            })
        }
        .padding({top: 40, left: 10, right: 10, bottom: 10})
        .width('100%')
        .height("100%")
      }
    
      // 计算数组总和
      private total(): number {
        let total = 0
        this.message2.forEach((value: number) => {
          total += value
        })
        return total
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40

    样例运行结果如下图所示:

    14_2_4_2
  • class、Object 类型

    当装饰的数据类型为 classObject 时,支持的操作为:重新赋值 操作,简单样例如下所示:

    class Headmaster {
      public name: string;
      constructor(name: string) {
        this.name = name;
      }
    }
    
    class Student {
      public name: string;
      constructor(name: string) {
        this.name = name;
      }
    }
    
    class Teacher {
      public headmaster: Headmaster;
      public students: Student[];
      public name: string;
      public age: number;
    
      constructor(headmaster: Headmaster, student: Student[], name: string, age: number) {
        this.headmaster = headmaster;
        this.students = student;
        this.name = name;
        this.age = age;
      }
    }
    
    @Entry @ComponentV2 struct Page_02_state {
    
      headmaster: Headmaster = new Headmaster("刘校长");
      student: Student = new Student("张三");
      @Local teacher: Teacher = new Teacher(this.headmaster, [this.student], "张老师", 26);
    
      build() {
        Column({space: 10}) {
          Text("老师姓名: " + this.teacher.name + "\n老师年龄:" + this.teacher.age + "\n老师领导:" + this.teacher.headmaster.name + "\n教有学生:" + this.teacher.students.length + "\n学生名字:" + this.studentNames())
            .fontSize(20)
          Button("重新赋值")
            .onClick(() => {
              this.teacher = new Teacher(new Headmaster("王校长"), [this.student], "王老师", 30);
            })
          Button("属性赋值")
            .onClick(() => {
              this.teacher.students = [new Student("李四"), new Student("王五")];
              this.teacher.age = 40;
              this.teacher.name = "新来的刘老师"
              this.teacher.headmaster = new Headmaster("新来的吕校长")
            })
          Button("嵌套赋值")
            .onClick(() => {
              this.teacher.headmaster.name = "新来的刘校长"
              this.teacher.students.push(new Student("新来的王同学"))
              this.teacher.students[0].name = "新来的刘同学";
              this.teacher.students.pop()
            })
        }
        .padding(10)
        .width('100%')
        .height("100%")
      }
    
      private studentNames(): string {
        let names = "";
        this.teacher.students.forEach((student) => {
          names = names.concat(student.name).concat(",");
        });
        return names;
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70

    样例运行结果如下图所示:

    14_2_4_3

    由样例运行结果可知,@Local 装饰器修饰的 class 或者 object,支持自身的 重新赋值 操作。

# 14.2.6:初始化顺序

@Local 装饰器装饰的状态数据初始化顺序为 本地初始化,就是在定义状态数据的时候直接赋予初始值,样例代码如下:

@Entry @ComponentV2 struct Page_02_state {

  @Local count: number = Math.random(); // 定义count时直接赋予初始值

  build() {
    Column({space: 10}) {
      Text("count: " + this.count)
        .fontSize(22)
        .onClick(() => {
          this.count = Math.random();
        })
    }
    .padding(10)
    .width("100%")
    .height("100%")
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

样例运行结果如下图所示:

14_2_5_1

# 14.2.7:完整样例

笔者结合 List 组件实现一个 添加删除重置局部更新 的小样例,代码如下所示:

class Student {
  public name: string;
  public age: number;

  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }
}

@Entry @ComponentV2 struct Page_02_state {

  @Local students: Array<Student> = [];

  build() {
    Column({space: 10}) {
      List({
        space: 10
      }) {
        ForEach(this.students, (item: Student, index: number) => {
          ListItem() {
            Row() {
              Text(index.toString())
                .width(50)
                .textAlign(TextAlign.Center)
              Column() {
                Text(item.name + index)
                  .fontSize(18)
                Text("age: " + item.age)
                  .fontSize(12)
              }
              .alignItems(HorizontalAlign.Start)
              .layoutWeight(1)
              .height("100%")
            }
            .borderRadius(10)
            .backgroundColor("#aabbcc")
            .width("100%")
            .height(45)
          }
          .width("100%")
          .height(45)
        })
      }
      .width("100%")
      .layoutWeight(1)
      Row({space: 5}) {
        Button("添加")
          .onClick(() => {
            this.students.push(new Student("张三", Math.round(Math.random() * 100 + 1)));
          })
        Button("删除")
          .onClick(() => {
            this.students.pop();
          })
        Button("重置")
          .onClick(() => {
            this.students = [];
          })
        Button("更新 1")
          .onClick(() => {
            this.students[this.students.length - 1] = new Student("新来的张三", 20);
          })
        Button("更新 2")
          .onClick(() => {
            this.students[this.students.length - 1].age = 110;
            this.students[this.students.length - 1].name = "新来的学生"
          })
      }
      .width("100%")
    }
    .padding(10)
    .width('100%')
    .height("100%")
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76

样例运行结果如下图所示:

14_2_6_1
请作者喝杯咖啡

津公网安备 12011402001367号

津ICP备2020008934号-2

中央网信办互联网违法和不良信息举报中心

天津市互联网违法和不良信息举报中心