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