1# \@State:组件内状态 2 3 4\@State装饰的变量,或称为状态变量,一旦变量拥有了状态属性,就和自定义组件的渲染绑定起来。当状态改变时,UI会发生对应的渲染改变。 5 6 7在状态变量相关装饰器中,\@State是最基础的,使变量拥有状态属性的装饰器,它也是大部分状态变量的数据源。 8 9 10> **说明:** 11> 12> 从API version 9开始,该装饰器支持在ArkTS卡片中使用。 13 14 15## 概述 16 17\@State装饰的变量,与声明式范式中的其他被装饰变量一样,是私有的,只能从组件内部访问,在声明时必须指定其类型和本地初始化。初始化也可选择使用命名参数机制从父组件完成初始化。 18 19\@State装饰的变量拥有以下特点: 20 21- \@State装饰的变量与子组件中的\@Prop、\@Link或\@ObjectLink装饰变量之间建立单向或双向数据同步。 22 23- \@State装饰的变量生命周期与其所属自定义组件的生命周期相同。 24 25 26## 装饰器使用规则说明 27 28| \@State变量装饰器 | 说明 | 29| ------------ | ---------------------------------------- | 30| 装饰器参数 | 无 | 31| 同步类型 | 不与父组件中任何类型的变量同步。 | 32| 允许装饰的变量类型 | Object、class、string、number、boolean、enum类型,以及这些类型的数组。嵌套类型的场景请参考[观察变化](#观察变化)。<br/>类型必须被指定。<br/>不支持any,不支持简单类型和复杂类型的联合类型,不允许使用undefined和null。<br/>**说明:**<br/>建议不要装饰Date类型,应用可能会产生异常行为。<br/>不支持Length、ResourceStr、ResourceColor类型,Length、ResourceStr、ResourceColor为简单类型和复杂类型的联合类型。 | 33| 被装饰变量的初始值 | 必须指定。 | 34 35 36## 变量的传递/访问规则说明 37 38| 传递/访问 | 说明 | 39| --------- | ---------------------------------------- | 40| 从父组件初始化 | 可选,从父组件初始化或者本地初始化。<br/>支持父组件中常规变量、\@State、\@Link、\@Prop、\@Provide、\@Consume、\@ObjectLink、\@StorageLink、\@StorageProp、\@LocalStorageLink和\@LocalStorageProp装饰的变量,初始化子组件的\@State。 | 41| 用于初始化子组件 | \@State装饰的变量支持初始化子组件的常规变量、\@State、\@Link、\@Prop、\@Provide。 | 42| 是否支持组件外访问 | 不支持,只能在组件内访问。 | 43 44 **图1** 初始化规则图示 45 46![zh-cn_image_0000001502091796](figures/zh-cn_image_0000001502091796.png) 47 48 49## 观察变化和行为表现 50 51并不是状态变量的所有更改都会引起UI的刷新,只有可以被框架观察到的修改才会引起UI刷新。该小节去介绍什么样的修改才能被观察到,以及观察到变化后,框架的是怎么引起UI刷新的,即框架的行为表现是什么。 52 53 54### 观察变化 55 56- 当装饰的数据类型为boolean、string、number类型时,可以观察到数值的变化。 57 58 ```ts 59 // for simple type 60 @State count: number = 0; 61 // value changing can be observed 62 this.count = 1; 63 ``` 64 65- 当装饰的数据类型为class或者Object时,可以观察到自身的赋值的变化,和其属性赋值的变化,即Object.keys(observedObject)返回的所有属性。例子如下。 66 声明ClassA和Model类。 67 68 ```ts 69 class ClassA { 70 public value: string; 71 72 constructor(value: string) { 73 this.value = value; 74 } 75 } 76 77 class Model { 78 public value: string; 79 public name: ClassA; 80 constructor(value: string, a: ClassA) { 81 this.value = value; 82 this.name = a; 83 } 84 } 85 ``` 86 87 \@State装饰的类型是Model 88 89 ```ts 90 // class类型 91 @State title: Model = new Model('Hello', new ClassA('World')); 92 ``` 93 94 对\@State装饰变量的赋值。 95 96 ```ts 97 // class类型赋值 98 this.title = new Model('Hi', new ClassA('ArkUI')); 99 ``` 100 101 对\@State装饰变量的属性赋值。 102 103 ```ts 104 // class属性的赋值 105 this.title.value = 'Hi' 106 ``` 107 108 嵌套属性的赋值观察不到。 109 110 ```ts 111 // 嵌套的属性赋值观察不到 112 this.title.name.value = 'ArkUI' 113 ``` 114- 当装饰的对象是array时,可以观察到数组本身的赋值和添加、删除、更新数组的变化。例子如下。 115 声明ClassA和Model类。 116 117 ```ts 118 class Model { 119 public value: number; 120 constructor(value: number) { 121 this.value = value; 122 } 123 } 124 ``` 125 126 \@State装饰的对象为Model类型数组时。 127 128 ```ts 129 @State title: Model[] = [new Model(11), new Model(1)] 130 ``` 131 132 数组自身的赋值可以观察到。 133 134 ```ts 135 this.title = [new Model(2)] 136 ``` 137 138 数组项的赋值可以观察到。 139 140 ```ts 141 this.title[0] = new Model(2) 142 ``` 143 144 删除数组项可以观察到。 145 146 ```ts 147 this.title.pop() 148 ``` 149 150 新增数组项可以观察到。 151 152 ```ts 153 this.title.push(new Model(12)) 154 ``` 155 156 157### 框架行为 158 159- 当状态变量被改变时,查询依赖该状态变量的组件; 160 161- 执行依赖该状态变量的组件的更新方法,组件更新渲染; 162 163- 和该状态变量不相关的组件或者UI描述不会发生重新渲染,从而实现页面渲染的按需更新。 164 165 166## 使用场景 167 168 169### 装饰简单类型的变量 170 171以下示例为\@State装饰的简单类型,count被\@State装饰成为状态变量,count的改变引起Button组件的刷新: 172 173- 当状态变量count改变时,查询到只有Button组件关联了它; 174 175- 执行Button组件的更新方法,实现按需刷新。 176 177 178```ts 179@Entry 180@Component 181struct MyComponent { 182 @State count: number = 0; 183 184 build() { 185 Button(`click times: ${this.count}`) 186 .onClick(() => { 187 this.count += 1; 188 }) 189 } 190} 191``` 192 193 194### 装饰class对象类型的变量 195 196- 自定义组件MyComponent定义了被\@State装饰的状态变量count和title,其中title的类型为自定义类Model。如果count或title的值发生变化,则查询MyComponent中使用该状态变量的UI组件,并进行重新渲染。 197 198- EntryComponent中有多个MyComponent组件实例,第一个MyComponent内部状态的更改不会影响第二个MyComponent。 199 200 201 202```ts 203class Model { 204 public value: string; 205 206 constructor(value: string) { 207 this.value = value; 208 } 209} 210 211@Entry 212@Component 213struct EntryComponent { 214 build() { 215 Column() { 216 // 此处指定的参数都将在初始渲染时覆盖本地定义的默认值,并不是所有的参数都需要从父组件初始化 217 MyComponent({ count: 1, increaseBy: 2 }) 218 MyComponent({ title: new Model('Hello, World 2'), count: 7 }) 219 } 220 } 221} 222 223@Component 224struct MyComponent { 225 @State title: Model = new Model('Hello World'); 226 @State count: number = 0; 227 private increaseBy: number = 1; 228 229 build() { 230 Column() { 231 Text(`${this.title.value}`) 232 Button(`Click to change title`).onClick(() => { 233 // @State变量的更新将触发上面的Text组件内容更新 234 this.title.value = this.title.value === 'Hello ArkUI' ? 'Hello World' : 'Hello ArkUI'; 235 }) 236 237 Button(`Click to increase count=${this.count}`).onClick(() => { 238 // @State变量的更新将触发该Button组件的内容更新 239 this.count += this.increaseBy; 240 }) 241 } 242 } 243} 244``` 245 246 247从该示例中,我们可以了解到\@State变量首次渲染的初始化流程: 248 249 2501. 使用默认的本地初始化: 251 252 ```ts 253 @State title: Model = new Model('Hello World'); 254 @State count: number = 0; 255 ``` 256 2572. 对于\@State来说,命名参数机制传递的值并不是必选的,如果没有命名参数传值,则使用本地初始化的默认值: 258 259 ```ts 260 MyComponent({ count: 1, increaseBy: 2 }) 261 ``` 262