1# \@BuilderParam装饰器:引用\@Builder函数 2 3 4当开发者创建了自定义组件,并想对该组件添加特定功能时,例如在自定义组件中添加一个点击跳转操作。若直接在组件内嵌入事件方法,将会导致所有引入该自定义组件的地方均增加了该功能。为解决此问题,ArkUI引入了\@BuilderParam装饰器,\@BuilderParam用来装饰指向[\@Builder](./arkts-builder.md)方法的变量(@BuilderParam是用来承接@Builder函数的)。开发者可以在初始化自定义组件时,使用不同的方式(如:参数修改、尾随闭包、借用箭头函数等)对\@BuilderParam装饰的自定义构建函数进行传参赋值,在自定义组件内部通过调用\@BuilderParam为组件增加特定的功能。该装饰器用于声明任意UI描述的一个元素,类似slot占位符。 5 6 7> **说明:** 8> 9> 从API version 9开始,该装饰器支持在ArkTS卡片中使用。 10> 11> 从API version 11开始,该装饰器支持在原子化服务中使用。 12 13## 限制条件 14 15@BuilderParam修饰的变量接收来自父组件通过\@Builder传递内容,且@Builder函数是参数传递类型,仅支持局部@Builder函数参数传递。 16 17## 装饰器使用说明 18 19 20### 初始化\@BuilderParam装饰的方法 21 22\@BuilderParam装饰的方法只能被自定义构建函数(\@Builder装饰的方法)初始化。如果在API 11中和[\@Require](arkts-require.md)结合使用,则必须父组件构造传参。 23 24- 使用所属自定义组件的自定义构建函数或者全局的自定义构建函数,在本地初始化\@BuilderParam。 25 26 ```ts 27 @Builder function overBuilder() {} 28 29 @Component 30 struct Child { 31 @Builder doNothingBuilder() {}; 32 33 // 使用自定义组件的自定义构建函数初始化@BuilderParam 34 @BuilderParam customBuilderParam: () => void = this.doNothingBuilder; 35 // 使用全局自定义构建函数初始化@BuilderParam 36 @BuilderParam customOverBuilderParam: () => void = overBuilder; 37 build(){} 38 } 39 ``` 40 41- 用父组件自定义构建函数初始化子组件\@BuilderParam装饰的方法。 42 43 ```ts 44 @Component 45 struct Child { 46 @Builder customBuilder() {}; 47 // 使用父组件@Builder装饰的方法初始化子组件@BuilderParam 48 @BuilderParam customBuilderParam: () => void = this.customBuilder; 49 50 build() { 51 Column() { 52 this.customBuilderParam() 53 } 54 } 55 } 56 57 @Entry 58 @Component 59 struct Parent { 60 @Builder componentBuilder() { 61 Text(`Parent builder `) 62 } 63 64 build() { 65 Column() { 66 Child({ customBuilderParam: this.componentBuilder }) 67 } 68 } 69 } 70 ``` 71 **图1** 示例效果图 72 73  74 75 76- 需要注意this的指向。 77 78以下示例对this的指向做了介绍。 79 80 ```ts 81 @Component 82 struct Child { 83 label: string = `Child`; 84 @Builder customBuilder() {}; 85 @Builder customChangeThisBuilder() {}; 86 @BuilderParam customBuilderParam: () => void = this.customBuilder; 87 @BuilderParam customChangeThisBuilderParam: () => void = this.customChangeThisBuilder; 88 89 build() { 90 Column() { 91 this.customBuilderParam() 92 this.customChangeThisBuilderParam() 93 } 94 } 95 } 96 97 @Entry 98 @Component 99 struct Parent { 100 label: string = `Parent`; 101 102 @Builder componentBuilder() { 103 Text(`${this.label}`) 104 } 105 106 build() { 107 Column() { 108 // 调用this.componentBuilder()时,this指向当前@Entry所装饰的Parent组件,即label变量的值为"Parent"。 109 this.componentBuilder() 110 Child({ 111 // 把this.componentBuilder传给子组件Child的@BuilderParam customBuilderParam,this指向的是子组件Child,即label变量的值为"Child"。 112 customBuilderParam: this.componentBuilder, 113 // 把():void=>{this.componentBuilder()}传给子组件Child的@BuilderParam customChangeThisBuilderParam, 114 // 因为箭头函数的this指向的是宿主对象,所以label变量的值为"Parent"。 115 customChangeThisBuilderParam: (): void => { this.componentBuilder() } 116 }) 117 } 118 } 119 } 120 ``` 121 **图2** 示例效果图 122 123  124 125 126## 使用场景 127 128 129### 参数初始化组件 130 131\@BuilderParam装饰的方法可以是有参数和无参数的两种形式,需与指向的\@Builder方法类型匹配。\@BuilderParam装饰的方法类型需要和\@Builder方法类型一致。 132 133```ts 134class Tmp{ 135 label: string = ''; 136} 137 138@Builder function overBuilder($$: Tmp) { 139 Text($$.label) 140 .width(400) 141 .height(50) 142 .backgroundColor(Color.Green) 143} 144 145@Component 146struct Child { 147 label: string = 'Child'; 148 @Builder customBuilder() {}; 149 // 无参数类型,指向的componentBuilder也是无参数类型 150 @BuilderParam customBuilderParam: () => void = this.customBuilder; 151 // 有参数类型,指向的overBuilder也是有参数类型的方法 152 @BuilderParam customOverBuilderParam: ($$: Tmp) => void = overBuilder; 153 154 build() { 155 Column() { 156 this.customBuilderParam() 157 this.customOverBuilderParam({label: 'global Builder label' } ) 158 } 159 } 160} 161 162@Entry 163@Component 164struct Parent { 165 label: string = 'Parent'; 166 167 @Builder componentBuilder() { 168 Text(`${this.label}`) 169 } 170 171 build() { 172 Column() { 173 this.componentBuilder() 174 Child({ customBuilderParam: this.componentBuilder, customOverBuilderParam: overBuilder }) 175 } 176 } 177} 178``` 179**图3** 示例效果图 180 181 182 183 184### 尾随闭包初始化组件 185 186在自定义组件中使用\@BuilderParam装饰的属性时也可通过尾随闭包进行初始化。在初始化自定义组件时,组件后紧跟一个大括号“{}”形成尾随闭包场景。 187 188> **说明:** 189> 190> - 此场景下自定义组件内有且仅有一个使用\@BuilderParam装饰的属性。 191> 192> - 此场景下自定义组件不支持使用通用属性。 193 194开发者可以将尾随闭包内的内容看做\@Builder装饰的函数传给\@BuilderParam。 195 196示例1: 197 198```ts 199@Component 200struct CustomContainer { 201 @Prop header: string = ''; 202 @Builder closerBuilder(){}; 203 // 使用父组件的尾随闭包{}(@Builder装饰的方法)初始化子组件@BuilderParam 204 @BuilderParam closer: () => void = this.closerBuilder; 205 206 build() { 207 Column() { 208 Text(this.header) 209 .fontSize(30) 210 this.closer() 211 } 212 } 213} 214 215@Builder function specificParam(label1: string, label2: string) { 216 Column() { 217 Text(label1) 218 .fontSize(30) 219 Text(label2) 220 .fontSize(30) 221 } 222} 223 224@Entry 225@Component 226struct CustomContainerUser { 227 @State text: string = 'header'; 228 229 build() { 230 Column() { 231 // 创建CustomContainer,在创建CustomContainer时,通过其后紧跟一个大括号“{}”形成尾随闭包 232 // 作为传递给子组件CustomContainer @BuilderParam closer: () => void的参数 233 CustomContainer({ header: this.text }) { 234 Column() { 235 specificParam('testA', 'testB') 236 }.backgroundColor(Color.Yellow) 237 .onClick(() => { 238 this.text = 'changeHeader'; 239 }) 240 } 241 } 242 } 243} 244``` 245**图4** 示例效果图 246 247 248 249使用全局@Builder和局部@Builder通过尾随闭包的形式去初始化@ComponentV2修饰的自定义组件中的@BuilderParam。 250 251示例2: 252 253```ts 254@ComponentV2 255struct ChildPage { 256 @Require @Param message: string = ""; 257 @Builder customBuilder() {}; 258 @BuilderParam customBuilderParam: () => void = this.customBuilder; 259 260 build() { 261 Column() { 262 Text(this.message) 263 .fontSize(30) 264 .fontWeight(FontWeight.Bold) 265 this.customBuilderParam() 266 } 267 } 268} 269 270const builder_value: string = 'Hello World'; 271@Builder function overBuilder() { 272 Row() { 273 Text(`全局 Builder: ${builder_value}`) 274 .fontSize(20) 275 .fontWeight(FontWeight.Bold) 276 } 277} 278 279@Entry 280@ComponentV2 281struct ParentPage { 282 @Local label: string = `Parent Page`; 283 284 @Builder componentBuilder() { 285 Row(){ 286 Text(`局部 Builder :${this.label}`) 287 .fontSize(20) 288 .fontWeight(FontWeight.Bold) 289 } 290 } 291 292 build() { 293 Column() { 294 ChildPage({ message: this.label}){ 295 Column() { // 使用局部@Builder,通过组件后紧跟一个大括号“{}”形成尾随闭包去初始化自定义组件@BuilderParam 296 this.componentBuilder(); 297 } 298 } 299 Line() 300 .width('100%') 301 .height(10) 302 .backgroundColor('#000000').margin(10) 303 ChildPage({ message: this.label}){ // 使用全部@Builder,通过组件后紧跟一个大括号“{}”形成尾随闭包去初始化自定义组件@BuilderParam 304 Column() { 305 overBuilder(); 306 } 307 } 308 } 309 } 310} 311``` 312 313### 使用全局和局部\@Builder初始化\@BuilderParam 314 315在自定义组件中,使用\@BuilderParam修饰的变量接收来自父组件通过\@Builder传递的内容进行初始化,因为父组件的\@Builder可以使用箭头函数的形式改变当前的this指向,所以当使用\@BuilderParam修饰的变量时,会展示出不同的内容。 316 317```ts 318@Component 319struct ChildPage { 320 label: string = `Child Page`; 321 @Builder customBuilder() {}; 322 @BuilderParam customBuilderParam: () => void = this.customBuilder; 323 @BuilderParam customChangeThisBuilderParam: () => void = this.customBuilder; 324 325 build() { 326 Column() { 327 this.customBuilderParam() 328 this.customChangeThisBuilderParam() 329 } 330 } 331} 332 333const builder_value: string = 'Hello World'; 334@Builder function overBuilder() { 335 Row() { 336 Text(`全局 Builder: ${builder_value}`) 337 .fontSize(20) 338 .fontWeight(FontWeight.Bold) 339 } 340} 341 342@Entry 343@Component 344struct ParentPage { 345 label: string = `Parent Page`; 346 347 @Builder componentBuilder() { 348 Row(){ 349 Text(`局部 Builder :${this.label}`) 350 .fontSize(20) 351 .fontWeight(FontWeight.Bold) 352 } 353 } 354 355 build() { 356 Column() { 357 // 调用this.componentBuilder()时,this指向当前@Entry所装饰的ParentPage组件,所以label变量的值为"Parent Page"。 358 this.componentBuilder() 359 ChildPage({ 360 // 把this.componentBuilder传给子组件ChildPage的@BuilderParam customBuilderParam,this指向的是子组件ChildPage,所以label变量的值为"Child Page"。 361 customBuilderParam: this.componentBuilder, 362 // 把():void=>{this.componentBuilder()}传给子组件ChildPage的@BuilderParam customChangeThisBuilderParam, 363 // 因为箭头函数的this指向的是宿主对象,所以label变量的值为"Parent Page"。 364 customChangeThisBuilderParam: (): void => { this.componentBuilder() } 365 }) 366 Line() 367 .width('100%') 368 .height(10) 369 .backgroundColor('#000000').margin(10) 370 // 调用全局overBuilder()时,this指向当前整个活动页,所以展示的内容为"Hello World"。 371 overBuilder() 372 ChildPage({ 373 // 把全局overBuilder传给子组件ChildPage的@BuilderParam customBuilderParam,this指向当前整个活动页,所以展示的内容为"Hello World"。 374 customBuilderParam: overBuilder, 375 // 把全局overBuilder传给子组件ChildPage的@BuilderParam customChangeThisBuilderParam,this指向当前整个活动页,所以展示的内容为"Hello World"。 376 customChangeThisBuilderParam: overBuilder 377 }) 378 } 379 } 380} 381``` 382**图5** 示例效果图 383 384 385 386### 在@ComponentV2修饰的自定义组件中使用@BuilderParam 387 388使用全局@Builder和局部@Builder去初始化@CompoentV2修饰的自定义组件中的@BuilderParam属性。 389 390```ts 391@ComponentV2 392struct ChildPage { 393 @Param label: string = `Child Page`; 394 @Builder customBuilder() {}; 395 @BuilderParam customBuilderParam: () => void = this.customBuilder; 396 @BuilderParam customChangeThisBuilderParam: () => void = this.customBuilder; 397 398 build() { 399 Column() { 400 this.customBuilderParam() 401 this.customChangeThisBuilderParam() 402 } 403 } 404} 405 406const builder_value: string = 'Hello World'; 407@Builder function overBuilder() { 408 Row() { 409 Text(`全局 Builder: ${builder_value}`) 410 .fontSize(20) 411 .fontWeight(FontWeight.Bold) 412 } 413} 414 415@Entry 416@ComponentV2 417struct ParentPage { 418 @Local label: string = `Parent Page`; 419 420 @Builder componentBuilder() { 421 Row(){ 422 Text(`局部 Builder :${this.label}`) 423 .fontSize(20) 424 .fontWeight(FontWeight.Bold) 425 } 426 } 427 428 build() { 429 Column() { 430 // 调用this.componentBuilder()时,this指向当前@Entry所装饰的ParentPage组件,所以label变量的值为"Parent Page"。 431 this.componentBuilder() 432 ChildPage({ 433 // 把this.componentBuilder传给子组件ChildPage的@BuilderParam customBuilderParam,this指向的是子组件ChildPage,所以label变量的值为"Child Page"。 434 customBuilderParam: this.componentBuilder, 435 // 把():void=>{this.componentBuilder()}传给子组件ChildPage的@BuilderParam customChangeThisBuilderPara 436 // 因为箭头函数的this指向的是宿主对象,所以label变量的值为"Parent Page"。 437 customChangeThisBuilderParam: (): void => { this.componentBuilder() } 438 }) 439 Line() 440 .width('100%') 441 .height(5) 442 .backgroundColor('#000000').margin(10) 443 // 调用全局overBuilder()时,this指向当前整个活动页,所以展示的内容为"Hello World"。 444 overBuilder() 445 ChildPage({ 446 // 把全局overBuilder传给子组件ChildPage的@BuilderParam customBuilderParam,this指向当前整个活动页,所以展示的内容为"Hello World"。 447 customBuilderParam: overBuilder, 448 // 把全局overBuilder传给子组件ChildPage的@BuilderParam customChangeThisBuilderParam,this指向当前整个活动页,所以展示的内容为"Hello World"。 449 customChangeThisBuilderParam: overBuilder 450 }) 451 } 452 } 453} 454``` 455**图6** 示例效果图 456 457 458 459 460## 常见问题 461 462### 改变内容UI不刷新 463 464当调用自定义组件ChildPage时,把\@Builder作为参数通过this.componentBuilder的形式传递,当前this会指向自定义组件内部,所以在父组件里面改变label的值,自定义组件ChildPage是感知不到的。 465 466【反例】 467 468```ts 469@Component 470struct ChildPage { 471 @State label: string = `Child Page`; 472 @Builder customBuilder() {}; 473 @BuilderParam customChangeThisBuilderParam: () => void = this.customBuilder; 474 475 build() { 476 Column() { 477 this.customChangeThisBuilderParam() 478 } 479 } 480} 481 482@Entry 483@Component 484struct ParentPage { 485 @State label: string = `Parent Page`; 486 487 @Builder componentBuilder() { 488 Row(){ 489 Text(`Builder :${this.label}`) 490 .fontSize(20) 491 .fontWeight(FontWeight.Bold) 492 } 493 } 494 495 build() { 496 Column() { 497 ChildPage({ 498 customChangeThisBuilderParam: this.componentBuilder // 此处传递参数导致 499 }) 500 Button('点击改变label内容') 501 .onClick(() => { 502 this.label = 'Hello World'; 503 }) 504 } 505 } 506} 507``` 508 509使用箭头函数的形式把\@Builder传递进自定义组件ChildPage中,当前this指向会停留在父组件ParentPage里,所以在父组件里改变label的值,自定义组件ChildPage会感知到并重新渲染UI。 510 511【正例】 512 513```ts 514@Component 515struct ChildPage { 516 @State label: string = `Child Page`; 517 @Builder customBuilder() {}; 518 @BuilderParam customChangeThisBuilderParam: () => void = this.customBuilder; 519 520 build() { 521 Column() { 522 this.customChangeThisBuilderParam() 523 } 524 } 525} 526 527@Entry 528@Component 529struct ParentPage { 530 @State label: string = `Parent Page`; 531 532 @Builder componentBuilder() { 533 Row(){ 534 Text(`Builder :${this.label}`) 535 .fontSize(20) 536 .fontWeight(FontWeight.Bold) 537 } 538 } 539 540 build() { 541 Column() { 542 ChildPage({ 543 customChangeThisBuilderParam: () => { this.componentBuilder() } 544 }) 545 Button('点击改变label内容') 546 .onClick(() => { 547 this.label = 'Hello World'; 548 }) 549 } 550 } 551} 552```