1# \@LocalBuilder装饰器: 维持组件父子关系 2 3当开发者使用@Builder做引用数据传递时,会考虑组件的父子关系,使用了bind(this)之后,组件的父子关系和状态管理的父子关系并不一致。为了解决组件的父子关系和状态管理的父子关系保持一致,引入@LocalBuilder装饰器。@LocalBuilder拥有和局部@Builder相同的功能,且比局部@Builder能够更好的确定组件的父子关系和状态管理的父子关系。 4 5 6> **说明:** 7> 8> 从API version 12开始支持。 9> 10> 11 12## 装饰器使用说明 13 14 15### 自定义组件内自定义构建函数 16 17定义的语法: 18 19 20```ts 21@LocalBuilder MyBuilderFunction() { ... } 22``` 23 24使用方法: 25 26 27```ts 28this.MyBuilderFunction() 29``` 30 31- 允许在自定义组件内定义一个或多个@LocalBuilder方法,该方法被认为是该组件的私有、特殊类型的成员函数。 32- 自定义构建函数可以在所属组件的build方法和其他自定义构建函数中调用,但不允许在组件外调用。 33- 在自定义函数体中,this指代当前所属组件,组件的状态变量可以在自定义构建函数内访问。建议通过this访问自定义组件的状态变量而不是参数传递。 34 35## 限制条件 36 37- @LocalBuilder只能在所属组件内声明,不允许全局声明。 38 39- @LocalBuilder不能被内置装饰器和自定义装饰器使用。 40 41- 自定义组件内的静态方法不能和@LocalBuilder一起使用。 42 43## @LocalBuilder和局部@Builder使用区别 44 45@Builder方法引用传参时,为了改变this指向,使用bind(this)后,会导致组件的父子关系和状态管理的父子关系不一致,但是@LocalBuilder是否使用bind(this),都不会改变组件的父子关系。[@LocalBuilder和@Builder区别说明](arkts-localBuilder.md#localbuilder和builder区别说明)。 46 47## 参数传递规则 48 49@LocalBuilder函数的参数传递有[按值传递](#按值传递参数)和[按引用传递](#按引用传递参数)两种,均需遵守以下规则: 50 51- 参数的类型必须与参数声明的类型一致,不允许undefined、null和返回undefined、null的表达式。 52 53- 在@LocalBuilder修饰的函数内部,不允许改变参数值。 54 55- \@LocalBuilder内UI语法遵循[UI语法规则](arkts-create-custom-components.md#build函数)。 56 57- 只有传入一个参数,且参数需要直接传入对象字面量才会按引用传递该参数,其余传递方式均为按值传递。 58 59 60### 按引用传递参数 61 62按引用传递参数时,传递的参数可为状态变量,且状态变量的改变会引起\@LocalBuilder方法内的UI刷新。 63 64使用场景: 65 66组件Parent内的@LocalBuilder方法在build函数内调用,按键值对写法进行传值,当点击Click me 时,@LocalBuilder内的Text文本内容会随着状态变量内容的改变而改变。 67 68```ts 69class ReferenceType { 70 paramString: string = ''; 71} 72 73@Entry 74@Component 75struct Parent { 76 @State variableValue: string = 'Hello World'; 77 78 @LocalBuilder 79 citeLocalBuilder(params: ReferenceType) { 80 Row() { 81 Text(`UseStateVarByReference: ${params.paramString} `) 82 } 83 }; 84 85 build() { 86 Column() { 87 this.citeLocalBuilder({ paramString: this.variableValue }); 88 Button('Click me').onClick(() => { 89 this.variableValue = 'Hi World'; 90 }) 91 } 92 } 93} 94``` 95 96按引用传递参数时,如果在\@LocalBuilder方法内调用自定义组件,ArkUI提供[$$](arkts-two-way-sync.md)作为按引用传递参数的范式。 97 98使用场景: 99 100组件Parent内的@LocalBuilder方法内调用自定义组件,且按照引用传递参数将值传递到自定义组件,当Parent组件内状态变量值发生变化时,@LocalBuilder方法内的自定义组件HelloComponent的message值也会发生变化。 101 102```ts 103class ReferenceType { 104 paramString: string = ''; 105} 106 107@Component 108struct HelloComponent { 109 @Prop message: string; 110 111 build() { 112 Row() { 113 Text(`HelloComponent===${this.message}`); 114 } 115 } 116} 117 118@Entry 119@Component 120struct Parent { 121 @State variableValue: string = 'Hello World'; 122 123 @LocalBuilder 124 citeLocalBuilder($$: ReferenceType) { 125 Row() { 126 Column() { 127 Text(`citeLocalBuilder===${$$.paramString}`); 128 HelloComponent({ message: $$.paramString }); 129 } 130 } 131 } 132 133 build() { 134 Column() { 135 this.citeLocalBuilder({ paramString: this.variableValue }); 136 Button('Click me').onClick(() => { 137 this.variableValue = 'Hi World'; 138 }) 139 } 140 } 141} 142``` 143 144 145### 按值传递参数 146 147调用\@LocalBuilder装饰的函数默认按值传递。当传递的参数为状态变量时,状态变量的改变不会引起\@LocalBuilder方法内的UI刷新。所以当使用状态变量的时候,推荐使用[按引用传递](#按引用传递参数)。 148 149使用场景: 150 151组件Parent将@State修饰的label值按照函数传参方式传递到@LocalBuilder函数内,此时@LocalBuilder函数获取到的值为普通变量值,所以改变@State修饰的label值时,@LocalBuilder函数内的值不会发生改变。 152 153 154```ts 155@Entry 156@Component 157struct Parent { 158 @State label: string = 'Hello'; 159 160 @LocalBuilder 161 citeLocalBuilder(paramA1: string) { 162 Row() { 163 Text(`UseStateVarByValue: ${paramA1} `) 164 } 165 } 166 167 build() { 168 Column() { 169 this.citeLocalBuilder(this.label); 170 } 171 } 172} 173``` 174 175## @LocalBuilder和@Builder区别说明 176 177函数componentBuilder被@Builder修饰时,显示效果是 “Child”,函数componentBuilder被@LocalBuilder修饰时,显示效果是“Parent”。 178 179说明: 180 181@Builder componentBuilder()通过this.componentBuilder的形式传给子组件@BuilderParam customBuilderParam,this指向在Child的label,即“Child”。 182 183@LocalBuilder componentBuilder()通过this.componentBuilder的形式传给子组件@BuilderParam customBuilderParam,this指向Parent的label,即“Parent”。 184 185```ts 186@Component 187struct Child { 188 label: string = `Child`; 189 @BuilderParam customBuilderParam: () => void; 190 191 build() { 192 Column() { 193 this.customBuilderParam() 194 } 195 } 196} 197 198@Entry 199@Component 200struct Parent { 201 label: string = `Parent`; 202 203 @Builder componentBuilder() { 204 Text(`${this.label}`) 205 } 206 207 // @LocalBuilder componentBuilder() { 208 // Text(`${this.label}`) 209 // } 210 211 build() { 212 Column() { 213 Child({ customBuilderParam: this.componentBuilder }) 214 } 215 } 216} 217``` 218 219## 使用场景 220 221### @LocalBuilder在@ComponentV2修饰的自定义组件中使用 222 223使用局部的@LocalBuilder在@ComponentV2修饰的自定义组件中调用,修改变量触发UI刷新。 224 225```ts 226@ObservedV2 227class Info { 228 @Trace name: string = ''; 229 @Trace age: number = 0; 230} 231 232@ComponentV2 233struct ChildPage { 234 @Require @Param childInfo: Info; 235 build() { 236 Column() { 237 Text(`自定义组件 name :${this.childInfo.name}`) 238 .fontSize(20) 239 .fontWeight(FontWeight.Bold) 240 Text(`自定义组件 age :${this.childInfo.age}`) 241 .fontSize(20) 242 .fontWeight(FontWeight.Bold) 243 } 244 } 245} 246 247@Entry 248@ComponentV2 249struct ParentPage { 250 info1: Info = { name: "Tom", age: 25 }; 251 @Local info2: Info = { name: "Tom", age: 25 }; 252 253 @LocalBuilder 254 privateBuilder() { 255 Column() { 256 Text(`局部LocalBuilder@Builder name :${this.info1.name}`) 257 .fontSize(20) 258 .fontWeight(FontWeight.Bold) 259 Text(`局部LocalBuilder@Builder age :${this.info1.age}`) 260 .fontSize(20) 261 .fontWeight(FontWeight.Bold) 262 } 263 } 264 265 @LocalBuilder 266 privateBuilderSecond() { 267 Column() { 268 Text(`局部LocalBuilder@Builder name :${this.info2.name}`) 269 .fontSize(20) 270 .fontWeight(FontWeight.Bold) 271 Text(`局部LocalBuilder@Builder age :${this.info2.age}`) 272 .fontSize(20) 273 .fontWeight(FontWeight.Bold) 274 } 275 } 276 build() { 277 Column() { 278 Text(`info1: ${this.info1.name} ${this.info1.age}`) // Text1 279 .fontSize(30) 280 .fontWeight(FontWeight.Bold) 281 this.privateBuilder() // 调用局部@Builder 282 Line() 283 .width('100%') 284 .height(10) 285 .backgroundColor('#000000').margin(10) 286 Text(`info2: ${this.info2.name} ${this.info2.age}`) // Text2 287 .fontSize(30) 288 .fontWeight(FontWeight.Bold) 289 this.privateBuilderSecond() // 调用局部@Builder 290 Line() 291 .width('100%') 292 .height(10) 293 .backgroundColor('#000000').margin(10) 294 Text(`info1: ${this.info1.name} ${this.info1.age}`) // Text1 295 .fontSize(30) 296 .fontWeight(FontWeight.Bold) 297 ChildPage({ childInfo: this.info1}) // 调用自定义组件 298 Line() 299 .width('100%') 300 .height(10) 301 .backgroundColor('#000000').margin(10) 302 Text(`info2: ${this.info2.name} ${this.info2.age}`) // Text2 303 .fontSize(30) 304 .fontWeight(FontWeight.Bold) 305 ChildPage({ childInfo: this.info2}) // 调用自定义组件 306 Line() 307 .width('100%') 308 .height(10) 309 .backgroundColor('#000000').margin(10) 310 Button("change info1&info2") 311 .onClick(() => { 312 this.info1 = { name: "Cat", age: 18} // Text1不会刷新,原因是没有装饰器修饰监听不到值的改变。 313 this.info2 = { name: "Cat", age: 18} // Text2会刷新,原因是有装饰器修饰,可以监听到值的改变。 314 }) 315 } 316 } 317} 318```