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