1# \@Builder装饰器:自定义构建函数 2<!--Kit: ArkUI--> 3<!--Subsystem: ArkUI--> 4<!--Owner: @zhangboren--> 5<!--Designer: @zhangboren--> 6<!--Tester: @TerryTsao--> 7<!--Adviser: @zhang_yixin13--> 8 9ArkUI提供轻量的UI元素复用机制\@Builder,其内部UI结构固定,仅与使用方进行数据传递。开发者可将重复使用的UI元素抽象成函数,在build函数中调用。 10 11\@Builder装饰的函数也称为“自定义构建函数”。 12 13在阅读本文档前,建议提前阅读:[基本语法概述](./arkts-basic-syntax-overview.md)、[声明式UI描述](./arkts-declarative-ui-description.md)、[自定义组件-创建自定义组件](./arkts-create-custom-components.md)。 14 15@Builder装饰器和@Component装饰器的区别: 16 171. @Builder装饰器用于封装可复用的UI结构,通过提取重复的布局代码提高开发效率。该装饰器严格禁止在其内部定义状态变量或使用生命周期函数,必须通过参数传递或者访问所属组件的状态变量完成数据交互。 18 192. 在ArkUI框架中,@Component装饰器作为封装复杂UI组件的核心机制,允许开发者通过组合多个基础组件来构建可复用的复合界面。该装饰器不仅支持内部状态变量的定义,还能完整管理组件的生命周期。 20 21> **说明:** 22> 23> 从API version 9开始,该装饰器支持在ArkTS卡片中使用。 24> 25> 从API version 11开始,该装饰器支持在原子化服务中使用。 26 27 28## 装饰器使用说明 29 30\@Builder装饰器有两种使用方式,分别是定义在自定义组件内部的[私有自定义构建函数](#私有自定义构建函数)和定义在全局的[全局自定义构建函数](#全局自定义构建函数)。 31 32### 私有自定义构建函数 33 34示例: 35 36```ts 37@Entry 38@Component 39struct BuilderDemo { 40 @Builder 41 showTextBuilder() { 42 // @Builder装饰此函数,使其能以链式调用的方式配置并构建Text组件 43 Text('Hello World') 44 .fontSize(30) 45 .fontWeight(FontWeight.Bold) 46 } 47 48 @Builder 49 showTextValueBuilder(param: string) { 50 Text(param) 51 .fontSize(30) 52 .fontWeight(FontWeight.Bold) 53 } 54 55 build() { 56 Column() { 57 // 无参数 58 this.showTextBuilder() 59 // 有参数 60 this.showTextValueBuilder('Hello @Builder') 61 } 62 } 63} 64``` 65 66使用方法: 67 68- 允许在自定义组件内定义一个或多个@Builder函数,该函数被认为是该组件的私有、特殊类型的成员函数。 69 70- 私有自定义构建函数允许在自定义组件内、build函数和其他自定义构建函数中调用。 71 72- 在自定义组件中,`this`指代当前所属组件,组件的状态变量可在自定义构建函数内访问。建议通过`this`访问组件的状态变量,而不是通过参数传递。 73 74### 全局自定义构建函数 75 76示例: 77 78```ts 79@Builder 80function showTextBuilder() { 81 Text('Hello World') 82 .fontSize(30) 83 .fontWeight(FontWeight.Bold) 84} 85@Entry 86@Component 87struct BuilderDemo { 88 build() { 89 Column() { 90 showTextBuilder() 91 } 92 } 93} 94``` 95 96- 如果不涉及组件状态变化,建议使用全局的自定义构建函数。 97 98- 全局自定义构建函数允许在build函数和其他自定义构建函数中调用。 99 100 101## 参数传递规则 102 103自定义构建函数的参数传递有[按值传递](#按值传递参数)和[按引用传递](#按引用传递参数)两种,均需遵守以下规则: 104 105- @Builder装饰的函数参数类型不允许为undefined、null和返回undefined、null的表达式。 106 107- 在\@Builder装饰的函数内部,不允许改变参数值。 108 109- \@Builder内UI语法遵循[UI语法规则](arkts-create-custom-components.md#build函数)。 110 111- 只有当传入一个参数且该参数直接传入对象字面量时,才会按引用传递,其他传递方式均为按值传递。 112 113### 按值传递参数 114 115调用\@Builder装饰的函数默认按值传递。当传递的参数为状态变量时,状态变量的改变不会引起\@Builder函数内的UI刷新。所以当使用状态变量的时候,推荐使用[按引用传递](#按引用传递参数)。 116 117```ts 118@Builder function overBuilder(paramA1: string) { 119 Row() { 120 Text(`UseStateVarByValue: ${paramA1} `) 121 } 122} 123@Entry 124@Component 125struct Parent { 126 @State label: string = 'Hello'; 127 build() { 128 Column() { 129 overBuilder(this.label) 130 } 131 } 132} 133``` 134 135### 按引用传递参数 136 137按引用传递参数时,传递的参数可为状态变量,且状态变量的改变会引起\@Builder函数内的UI刷新。 138 139```ts 140class Tmp { 141 paramA1: string = ''; 142} 143 144@Builder 145function overBuilder(params: Tmp) { 146 Row() { 147 Text(`UseStateVarByReference: ${params.paramA1} `) 148 } 149} 150 151@Entry 152@Component 153struct Parent { 154 @State label: string = 'Hello'; 155 156 build() { 157 Column() { 158 // 在父组件中调用overBuilder组件时, 159 // 把this.label通过引用传递的方式传给overBuilder组件。 160 overBuilder({ paramA1: this.label }) 161 Button('Click me').onClick(() => { 162 // 单击Click me后,UI文本从Hello更改为ArkUI。 163 this.label = 'ArkUI'; 164 }) 165 } 166 } 167} 168``` 169 170## 限制条件 171 1721. \@Builder装饰的函数内部在没有使用[MutableBinding](../../reference/apis-arkui/js-apis-StateManagement.md#mutablebindingt20)时不允许修改参数值,修改不会触发UI刷新。若[按引用传递参数](#按引用传递参数)且仅传入一个参数时,修改参数内部的属性会抛出运行时错误。使用MutableBinding可以帮助开发者在\@Builder装饰的函数内部修改参数值,请参考[在@Builder装饰的函数内部修改入参内容](#在builder装饰的函数内部修改入参内容)。 173 1742. \@Builder按引用传递且仅传入一个参数时,才会触发动态渲染UI。请参考[按引用传递参数](#按引用传递参数)。 175 1763. 如果\@Builder传入的参数是两个或两个以上,不会触发动态渲染UI,请参考[@Builder存在两个或者两个以上参数](#builder存在两个或者两个以上参数)。 177 1784. \@Builder传入的参数中同时包含按值传递和按引用传递,不会触发动态渲染UI,请参考[@Builder存在两个或者两个以上参数](#builder存在两个或者两个以上参数)。 179 1805. \@Builder的参数必须按照对象字面量的形式,把所需属性一一传入,才会触发动态渲染UI,请参考[@Builder存在两个或者两个以上参数](#builder存在两个或者两个以上参数)。 181 182 183## 使用场景 184 185### 自定义组件内使用自定义构建函数 186 187创建私有的`@Builder`函数,在`Column`中使用`this.builder()`调用。通过`aboutToAppear`生命周期函数和按钮的点击事件更新`builder_value`,实现UI的动态渲染。 188 189```ts 190@Entry 191@Component 192struct PrivateBuilder { 193 @State builder_value: string = 'Hello'; 194 195 @Builder 196 builder() { 197 Column() { 198 Text(this.builder_value) 199 .width(230) 200 .height(40) 201 .backgroundColor('#ffeae5e5') 202 .borderRadius(20) 203 .margin(12) 204 .textAlign(TextAlign.Center) 205 } 206 } 207 208 aboutToAppear(): void { 209 setTimeout(() => { 210 this.builder_value = 'Hello World'; 211 }, 2000); 212 } 213 214 build() { 215 Row() { 216 Column() { 217 Text(this.builder_value) 218 .width(230) 219 .height(40) 220 .backgroundColor('#ffeae5e5') 221 .borderRadius(20) 222 .textAlign(TextAlign.Center) 223 this.builder() 224 Button('点击改变builder_value内容') 225 .onClick(() => { 226 this.builder_value = 'builder_value被点击了'; 227 }) 228 } 229 .height('100%') 230 .width('100%') 231 } 232 } 233} 234``` 235示例效果图 236 237 238 239### 全局自定义构建函数 240 241创建全局的\@Builder函数,在Column里面使用overBuilder()方式调用,通过以对象字面量的形式传递参数,无论是简单类型还是复杂类型,值的改变都会引起UI界面的刷新。 242 243```ts 244class ChildTmp { 245 val: number = 1; 246} 247 248class Tmp { 249 str_value: string = 'Hello'; 250 num_value: number = 0; 251 tmp_value: ChildTmp = new ChildTmp(); 252 arrayTmp_value: Array<ChildTmp> = []; 253} 254 255@Builder 256function overBuilder(param: Tmp) { 257 Column() { 258 Text(`str_value: ${param.str_value}`) 259 .width(230) 260 .height(40) 261 .margin(12) 262 .backgroundColor('#0d000000') 263 .fontColor('#e6000000') 264 .borderRadius(20) 265 .textAlign(TextAlign.Center) 266 Text(`num_value: ${param.num_value}`) 267 .width(230) 268 .height(40) 269 .margin(12) 270 .backgroundColor('#0d000000') 271 .fontColor('#e6000000') 272 .borderRadius(20) 273 .textAlign(TextAlign.Center) 274 Text(`tmp_value: ${param.tmp_value.val}`) 275 .width(230) 276 .height(40) 277 .margin(12) 278 .backgroundColor('#0d000000') 279 .fontColor('#e6000000') 280 .borderRadius(20) 281 .textAlign(TextAlign.Center) 282 ForEach(param.arrayTmp_value, (item: ChildTmp) => { 283 ListItem() { 284 Text(`arrayTmp_value: ${item.val}`) 285 .width(230) 286 .height(40) 287 .margin(12) 288 .backgroundColor('#0d000000') 289 .fontColor('#e6000000') 290 .borderRadius(20) 291 .textAlign(TextAlign.Center) 292 } 293 }, (item: ChildTmp) => JSON.stringify(item)) 294 } 295} 296 297@Entry 298@Component 299struct Parent { 300 @State objParam: Tmp = new Tmp(); 301 302 build() { 303 Column() { 304 Text('通过调用@Builder渲染UI界面') 305 .fontSize(20) 306 .margin(12) 307 overBuilder({ 308 str_value: this.objParam.str_value, 309 num_value: this.objParam.num_value, 310 tmp_value: this.objParam.tmp_value, 311 arrayTmp_value: this.objParam.arrayTmp_value 312 }) 313 Button('点击改变参数值').onClick(() => { 314 this.objParam.str_value = 'Hello World'; 315 this.objParam.num_value = 1; 316 this.objParam.tmp_value.val = 8; 317 const child_value: ChildTmp = { 318 val: 2 319 } 320 this.objParam.arrayTmp_value.push(child_value); 321 }) 322 } 323 .height('100%') 324 .width('100%') 325 } 326} 327``` 328示例效果图 329 330 331 332### 修改装饰器修饰的变量触发UI刷新 333 334在该场景中,`@Builder`被用来展示Text组件,不会参与动态UI刷新。Text组件中值的变化是通过使用装饰器的特性,监听到值的改变触发的UI刷新,而不是通过`@Builder`的能力触发的。 335 336```ts 337class Tmp { 338 str_value: string = 'Hello'; 339} 340 341@Entry 342@Component 343struct Parent { 344 @State objParam: Tmp = new Tmp(); 345 @State label: string = 'World'; 346 347 @Builder 348 privateBuilder() { 349 Column() { 350 Text(`wrapBuilder str_value: ${this.objParam.str_value}`) 351 .width(350) 352 .height(40) 353 .margin(12) 354 .backgroundColor('#0d000000') 355 .fontColor('#e6000000') 356 .borderRadius(20) 357 .textAlign(TextAlign.Center) 358 Text(`wrapBuilder num: ${this.label}`) 359 .width(350) 360 .height(40) 361 .margin(12) 362 .backgroundColor('#0d000000') 363 .fontColor('#e6000000') 364 .borderRadius(20) 365 .textAlign(TextAlign.Center) 366 } 367 } 368 369 build() { 370 Column() { 371 Text('通过调用@Builder渲染UI界面') 372 .fontSize(20) 373 this.privateBuilder() 374 Button('点击改变参数值').onClick(() => { 375 this.objParam.str_value = 'str_value Hello World'; 376 this.label = 'label Hello World'; 377 }) 378 } 379 .height('100%') 380 .width('100%') 381 } 382} 383``` 384示例效果图 385 386 387 388### 将@Builder装饰的函数当作customBuilder类型使用 389 390当参数类型为`CustomBuilder`时,可以传入定义的`@Builder`函数。因为`CustomBuilder`实际上是`Function(() => any)`或`void`类型,而`@Builder`也是`Function`类型。所以通过传入`@Builder`可以实现特定效果。 391全局`@Builder`函数当作`CustomBuilder`类型传递时需要绑定this上下文,开发者可以直接调用全局`@Builder`函数,编译工具链会自动生成绑定this上下文的代码。 392 393```ts 394@Builder 395function overBuilder() { 396 Row() { 397 Text('全局 Builder') 398 .fontSize(30) 399 .fontWeight(FontWeight.Bold) 400 } 401} 402 403@Entry 404@Component 405struct customBuilderDemo { 406 @State arr: number[] = [0, 1, 2, 3, 4]; 407 408 @Builder 409 privateBuilder() { 410 Row() { 411 Text('局部 Builder') 412 .fontSize(30) 413 .fontWeight(FontWeight.Bold) 414 } 415 } 416 417 build() { 418 Column() { 419 List({ space: 10 }) { 420 ForEach(this.arr, (item: number) => { 421 ListItem() { 422 Text(`${item}`) 423 .width('100%') 424 .height(100) 425 .fontSize(16) 426 .textAlign(TextAlign.Center) 427 .borderRadius(10) 428 .backgroundColor(0xFFFFFF) 429 } 430 .swipeAction({ 431 start: { 432 builder: overBuilder() // 编译工具链会自动绑定this上下文 433 }, 434 end: { 435 builder: () => { 436 // 在箭头函数中调用局部@Builder会自动绑定this上下文,无需编译工具链处理 437 this.privateBuilder() 438 } 439 } 440 }) 441 }, (item: number) => JSON.stringify(item)) 442 } 443 } 444 } 445} 446``` 447示例效果图 448 449 450 451### 多层\@Builder函数嵌套 452 453在\@Builder函数内调用自定义组件或者其他\@Builder函数,以实现多个\@Builder嵌套使用的场景,若要实现最内层的\@Builder动态UI刷新功能,必须要保证每层调用\@Builder的地方使用按引用传递的方式。这里的[`$$`](./arkts-two-way-sync.md)不是必须的参数形式,[`$$`](./arkts-two-way-sync.md)也可以换成其他名称。 454 455```ts 456class Tmp { 457 paramA1: string = ''; 458} 459 460@Builder 461function parentBuilder($$: Tmp) { 462 Row() { 463 Column() { 464 Text(`parentBuilder===${$$.paramA1}`) 465 .width(300) 466 .height(40) 467 .margin(10) 468 .backgroundColor('#0d000000') 469 .fontColor('#e6000000') 470 .borderRadius(20) 471 .textAlign(TextAlign.Center) 472 HelloComponent({ message: $$.paramA1 }) 473 childBuilder({ paramA1: $$.paramA1 }) 474 } 475 } 476} 477 478@Component 479struct HelloComponent { 480 @Prop message: string = ''; 481 482 build() { 483 Row() { 484 Text(`HelloComponent===${this.message}`) 485 .width(300) 486 .height(40) 487 .margin(10) 488 .backgroundColor('#0d000000') 489 .fontColor('#e6000000') 490 .borderRadius(20) 491 .textAlign(TextAlign.Center) 492 } 493 } 494} 495 496@Builder 497function childBuilder($$: Tmp) { 498 Row() { 499 Column() { 500 Text(`childBuilder===${$$.paramA1}`) 501 .width(300) 502 .height(40) 503 .margin(10) 504 .backgroundColor('#0d000000') 505 .fontColor('#e6000000') 506 .borderRadius(20) 507 .textAlign(TextAlign.Center) 508 HelloChildComponent({ message: $$.paramA1 }) 509 grandsonBuilder({ paramA1: $$.paramA1 }) 510 } 511 } 512} 513 514@Component 515struct HelloChildComponent { 516 @Prop message: string = ''; 517 518 build() { 519 Row() { 520 Text(`HelloChildComponent===${this.message}`) 521 .width(300) 522 .height(40) 523 .margin(10) 524 .backgroundColor('#0d000000') 525 .fontColor('#e6000000') 526 .borderRadius(20) 527 .textAlign(TextAlign.Center) 528 } 529 } 530} 531 532@Builder 533function grandsonBuilder($$: Tmp) { 534 Row() { 535 Column() { 536 Text(`grandsonBuilder===${$$.paramA1}`) 537 .width(300) 538 .height(40) 539 .margin(10) 540 .backgroundColor('#0d000000') 541 .fontColor('#e6000000') 542 .borderRadius(20) 543 .textAlign(TextAlign.Center) 544 HelloGrandsonComponent({ message: $$.paramA1 }) 545 } 546 } 547} 548 549@Component 550struct HelloGrandsonComponent { 551 @Prop message: string; 552 553 build() { 554 Row() { 555 Text(`HelloGrandsonComponent===${this.message}`) 556 .width(300) 557 .height(40) 558 .margin(10) 559 .backgroundColor('#0d000000') 560 .fontColor('#e6000000') 561 .borderRadius(20) 562 .textAlign(TextAlign.Center) 563 } 564 } 565} 566 567@Entry 568@Component 569struct Parent { 570 @State label: string = 'Hello'; 571 572 build() { 573 Column() { 574 parentBuilder({ paramA1: this.label }) 575 Button('Click me').onClick(() => { 576 this.label = 'ArkUI'; 577 }) 578 } 579 .height('100%') 580 .width('100%') 581 } 582} 583``` 584示例效果图 585 586 587 588### \@Builder函数联合V2装饰器 589 590由`@ObservedV2`和`@Trace`装饰的类对象实例具备深度观测属性变化的能力。在`@ComponentV2`装饰的自定义组件中,当调用全局Builder或局部Builder且使用值传递的方式传递参数时,修改`@Trace`装饰的对象属性可以触发UI刷新。 591 592```ts 593@ObservedV2 594class Info { 595 @Trace name: string; 596 @Trace age: number; 597 598 constructor(name: string, age: number) { 599 this.name = name; 600 this.age = age; 601 } 602} 603 604@Builder 605function overBuilder(param: Info) { 606 Column() { 607 Text(`全局@Builder name: ${param.name}`) 608 Text(`全局@Builder age: ${param.age}`) 609 } 610 .width(230) 611 .height(40) 612 .margin(10) 613 .padding({ left: 20 }) 614 .backgroundColor('#0d000000') 615 .borderRadius(20) 616} 617 618@ComponentV2 619struct ChildPage { 620 @Require @Param childInfo: Info; 621 622 build() { 623 Column() { 624 // 此处必须为值传递方式,如果使用引用传递的方式会被ArkTS语法拦截 625 overBuilder(this.childInfo) 626 } 627 } 628} 629 630@Entry 631@ComponentV2 632struct ParentPage { 633 info1: Info = new Info('Tom', 25); 634 info2: Info = new Info('Tom', 25); 635 636 @Builder 637 privateBuilder() { 638 Column() { 639 Text(`局部@Builder name: ${this.info1.name}`) 640 Text(`局部@Builder age: ${this.info1.age}`) 641 } 642 .width(230) 643 .height(40) 644 .margin(10) 645 .backgroundColor('#0d000000') 646 .borderRadius(20) 647 } 648 649 build() { 650 Column() { 651 Flex() { 652 Column() { 653 Text(`info1: ${this.info1.name} ${this.info1.age}`) // Text1 654 Text(`info2: ${this.info2.name} ${this.info2.age}`) // Text2 655 } 656 } 657 .width(230) 658 .height(40) 659 .margin(10) 660 .padding({ left: 60 }) 661 .backgroundColor('#0d000000') 662 .borderRadius(20) 663 664 // 调用局部@Builder 665 this.privateBuilder() 666 // 调用全局@Builder, 此处必须为值传递方式,如果使用引用传递的方式会被ArkTS语法拦截 667 overBuilder(this.info2) 668 ChildPage({ childInfo: this.info1 }) // 调用自定义组件 669 ChildPage({ childInfo: this.info2 }) // 调用自定义组件 670 Button('change info1&info2') 671 .onClick(() => { 672 this.info1.name = 'Cat'; // 修改Text1显示的info1的name值 673 this.info1.age = 18; // 修改Text1显示的info1的age值 674 this.info2.name = 'Cat'; // 修改Text2显示的info2的name值 675 this.info2.age = 18; // 修改Text2显示的info2的age值 676 }) 677 } 678 .height('100%') 679 .width('100%') 680 } 681} 682``` 683示例效果图 684 685 686 687当通过引用传递方式向`@Builder`传递参数时,若参数为`@Local`装饰的对象,对该对象进行整体赋值会触发`@Builder`中UI刷新。 688 689```ts 690class Info { 691 name: string = 'Tom'; 692 age: number = 25; 693} 694 695@Builder 696function overBuilder(param: Info) { 697 Column() { 698 Text(`全局@Builder name: ${param.name}`) 699 Text(`全局@Builder age: ${param.age}`) 700 } 701 .width(230) 702 .height(40) 703 .margin(10) 704 .padding({ left: 20 }) 705 .backgroundColor('#0d000000') 706 .borderRadius(20) 707} 708 709@ComponentV2 710struct ChildPage { 711 @Require @Param childInfo: Info; 712 713 build() { 714 Column() { 715 // 此处为引用传递方式 716 overBuilder({ name: this.childInfo.name, age: this.childInfo.age }) 717 } 718 } 719} 720 721@Entry 722@ComponentV2 723struct ParentPage { 724 info1: Info = { name: 'Tom', age: 25 }; 725 @Local info2: Info = { name: 'Tom', age: 25 }; 726 727 @Builder 728 privateBuilder() { 729 Column() { 730 Text(`局部@Builder name: ${this.info1.name}`) 731 Text(`局部@Builder age: ${this.info1.age}`) 732 } 733 .width(230) 734 .height(40) 735 .margin(10) 736 .backgroundColor('#0d000000') 737 .borderRadius(20) 738 } 739 740 build() { 741 Column() { 742 Flex() { 743 Column() { 744 Text(`info1: ${this.info1.name} ${this.info1.age}`) // Text1 745 Text(`info2: ${this.info2.name} ${this.info2.age}`) // Text2 746 } 747 } 748 .width(230) 749 .height(40) 750 .margin(10) 751 .padding({ left: 60 }) 752 .backgroundColor('#0d000000') 753 .borderRadius(20) 754 755 // 调用局部@Builder 756 this.privateBuilder() 757 // 调用全局@Builder, 此处为引用传递方式 758 overBuilder({ name: this.info2.name, age: this.info2.age }) 759 ChildPage({ childInfo: this.info1 }) // 调用自定义组件 760 ChildPage({ childInfo: this.info2 }) // 调用自定义组件 761 Button('change info1&info2') 762 .onClick(() => { 763 this.info1 = { name: 'Cat', age: 18 }; // Text1不会刷新,原因是没有装饰器修饰监听不到值的改变 764 this.info2 = { name: 'Cat', age: 18 }; // Text2会刷新,原因是有装饰器修饰,可以监听到值的改变 765 }) 766 } 767 .height('100%') 768 .width('100%') 769 } 770} 771``` 772示例效果图 773 774 775 776### 跨组件复用的全局\@Builder 777 778在跨组件的场景中调用全局\@Builder,通过按引用传递的方式传递参数,可以实现UI的动态刷新功能。 779 780```ts 781class Tmp { 782 componentName: string = 'Child'; 783} 784 785@Builder 786function itemBuilder(params: Tmp) { 787 Column() { 788 Text(`Builder ===${params.componentName}`) 789 .width(300) 790 .height(40) 791 .margin(10) 792 .backgroundColor('#0d000000') 793 .fontColor('#e6000000') 794 .borderRadius(20) 795 .textAlign(TextAlign.Center) 796 } 797} 798 799@Entry 800@Component 801struct ReusablePage { 802 @State switchFlag: boolean = true; 803 804 build() { 805 Column() { 806 if (this.switchFlag) { 807 ReusableChildPage({ message: 'Child' }) 808 } else { 809 ReusableChildTwoPage({ message: 'ChildTwo' }) 810 } 811 Button('Click me') 812 .onClick(() => { 813 this.switchFlag = !this.switchFlag; 814 }) 815 } 816 .height('100%') 817 .width('100%') 818 } 819} 820 821@Reusable 822@Component 823struct ReusableChildPage { 824 @State message: string = 'Child'; 825 826 aboutToReuse(params: Record<string, ESObject>): void { 827 console.info('Recycle ====Child'); 828 this.message = params.message; 829 } 830 831 build() { 832 Column() { 833 Text(`ReusableChildPage ===${this.message}`) 834 .width(300) 835 .height(40) 836 .margin(10) 837 .backgroundColor('#0d000000') 838 .fontColor('#e6000000') 839 .borderRadius(20) 840 .textAlign(TextAlign.Center) 841 itemBuilder({ componentName: this.message }) 842 } 843 } 844} 845 846@Reusable 847@Component 848struct ReusableChildTwoPage { 849 @State message: string = 'ChildTwo'; 850 851 aboutToReuse(params: Record<string, ESObject>): void { 852 console.info('Recycle ====ChildTwo'); 853 this.message = params.message; 854 } 855 856 build() { 857 Column() { 858 Text(`ReusableChildTwoPage ===${this.message}`) 859 .width(300) 860 .height(40) 861 .margin(10) 862 .backgroundColor('#0d000000') 863 .fontColor('#e6000000') 864 .borderRadius(20) 865 .textAlign(TextAlign.Center) 866 itemBuilder({ componentName: this.message }) 867 } 868 } 869} 870``` 871示例效果图 872 873 874 875### \@Builder支持状态变量刷新 876 877从API version 20开始,开发者可以通过使用`UIUtils.makeBinding()`函数、`Binding`类和`MutableBinding`类实现\@Builder函数中状态变量的刷新。详情请参考[状态管理API文档](../../reference/apis-arkui/js-apis-StateManagement.md#makebinding20)。 878 879```ts 880import { Binding, MutableBinding, UIUtils } from '@kit.ArkUI'; 881 882@ObservedV2 883class ClassA { 884 @Trace props: string = 'Hello'; 885} 886 887@Builder 888function CustomButton(num1: Binding<number>, num2: MutableBinding<number>) { 889 Row() { 890 Column() { 891 Text(`number1 === ${num1.value}, number2 === ${num2.value}`) 892 .width(300) 893 .height(40) 894 .margin(10) 895 .backgroundColor('#0d000000') 896 .fontColor('#e6000000') 897 .borderRadius(20) 898 .textAlign(TextAlign.Center) 899 900 Button(`only change number2`) 901 .onClick(() => { 902 num2.value += 1; 903 }) 904 } 905 } 906} 907 908@Builder 909function CustomButtonObj(obj1: MutableBinding<ClassA>) { 910 Row() { 911 Column() { 912 Text(`props === ${obj1.value.props}`) 913 .width(300) 914 .height(40) 915 .margin(10) 916 .backgroundColor('#0d000000') 917 .fontColor('#e6000000') 918 .borderRadius(20) 919 .textAlign(TextAlign.Center) 920 921 Button(`change props`) 922 .onClick(() => { 923 obj1.value.props += 'Hi'; 924 }) 925 } 926 } 927} 928 929@Entry 930@ComponentV2 931struct Single { 932 @Local number1: number = 5; 933 @Local number2: number = 12; 934 @Local classA: ClassA = new ClassA(); 935 936 build() { 937 Column() { 938 Button(`change both number1 and number2`) 939 .onClick(() => { 940 this.number1 += 1; 941 this.number2 += 2; 942 }) 943 Text(`number1 === ${this.number1}`) 944 .width(300) 945 .height(40) 946 .margin(10) 947 .backgroundColor('#0d000000') 948 .fontColor('#e6000000') 949 .borderRadius(20) 950 .textAlign(TextAlign.Center) 951 Text(`number2 === ${this.number2}`) 952 .width(300) 953 .height(40) 954 .margin(10) 955 .backgroundColor('#0d000000') 956 .fontColor('#e6000000') 957 .borderRadius(20) 958 .textAlign(TextAlign.Center) 959 CustomButton( 960 UIUtils.makeBinding<number>(() => this.number1), 961 UIUtils.makeBinding<number>( 962 () => this.number2, 963 (val: number) => { 964 this.number2 = val; 965 }) 966 ) 967 Text(`classA.props === ${this.classA.props}`) 968 .width(300) 969 .height(40) 970 .margin(10) 971 .backgroundColor('#0d000000') 972 .fontColor('#e6000000') 973 .borderRadius(20) 974 .textAlign(TextAlign.Center) 975 CustomButtonObj( 976 UIUtils.makeBinding<ClassA>( 977 () => this.classA, 978 (val: ClassA) => { 979 this.classA = val; 980 }) 981 ) 982 } 983 .width('100%') 984 .height('100%') 985 .alignItems(HorizontalAlign.Center) 986 .justifyContent(FlexAlign.Center) 987 } 988} 989``` 990示例效果图 991 992 993 994## 常见问题 995 996### \@Builder存在两个或者两个以上参数 997 998当存在两个或两个以上的参数时,即使通过对象字面量形式传递,值的改变也不会触发UI刷新。 999 1000【反例】 1001 1002```ts 1003class GlobalTmp { 1004 str_value: string = 'Hello'; 1005} 1006 1007@Builder function overBuilder(param: GlobalTmp, num: number) { 1008 Column() { 1009 Text(`str_value: ${param.str_value}`) 1010 Text(`num: ${num}`) 1011 } 1012} 1013 1014@Entry 1015@Component 1016struct Parent { 1017 @State objParam: GlobalTmp = new GlobalTmp(); 1018 @State num: number = 0; 1019 build() { 1020 Column() { 1021 Text('通过调用@Builder渲染UI界面') 1022 .fontSize(20) 1023 // 使用了两个参数,用法错误。 1024 overBuilder({str_value: this.objParam.str_value}, this.num) 1025 Line() 1026 .width('100%') 1027 .height(10) 1028 .backgroundColor('#000000').margin(10) 1029 Button('点击改变参数值').onClick(() => { 1030 this.objParam.str_value = 'Hello World'; 1031 this.num = 1; 1032 }) 1033 } 1034 } 1035} 1036``` 1037 1038【反例】 1039 1040```ts 1041class GlobalTmp { 1042 str_value: string = 'Hello'; 1043} 1044class SecondTmp { 1045 num_value: number = 0; 1046} 1047@Builder function overBuilder(param: GlobalTmp, num: SecondTmp) { 1048 Column() { 1049 Text(`str_value: ${param.str_value}`) 1050 Text(`num: ${num.num_value}`) 1051 } 1052} 1053 1054@Entry 1055@Component 1056struct Parent { 1057 @State strParam: GlobalTmp = new GlobalTmp(); 1058 @State numParam: SecondTmp = new SecondTmp(); 1059 build() { 1060 Column() { 1061 Text('通过调用@Builder渲染UI界面') 1062 .fontSize(20) 1063 // 使用了两个参数,用法错误。 1064 overBuilder({str_value: this.strParam.str_value}, {num_value: this.numParam.num_value}) 1065 Line() 1066 .width('100%') 1067 .height(10) 1068 .backgroundColor('#000000').margin(10) 1069 Button('点击改变参数值').onClick(() => { 1070 this.strParam.str_value = 'Hello World'; 1071 this.numParam.num_value = 1; 1072 }) 1073 } 1074 } 1075} 1076``` 1077 1078\@Builder只接受一个参数,当传入一个参数的时候,通过对象字面量的形式传递,值的改变会引起UI的刷新。 1079 1080【正例】 1081 1082```ts 1083class GlobalTmp { 1084 str_value: string = 'Hello'; 1085 num_value: number = 0; 1086} 1087@Builder function overBuilder(param: GlobalTmp) { 1088 Column() { 1089 Text(`str_value: ${param.str_value}`) 1090 Text(`num: ${param.num_value}`) 1091 } 1092} 1093 1094@Entry 1095@Component 1096struct Parent { 1097 @State objParam: GlobalTmp = new GlobalTmp(); 1098 build() { 1099 Column() { 1100 Text('通过调用@Builder渲染UI界面') 1101 .fontSize(20) 1102 overBuilder({str_value: this.objParam.str_value, num_value: this.objParam.num_value}) 1103 Line() 1104 .width('100%') 1105 .height(10) 1106 .backgroundColor('#000000').margin(10) 1107 Button('点击改变参数值').onClick(() => { 1108 this.objParam.str_value = 'Hello World'; 1109 this.objParam.num_value = 1; 1110 }) 1111 } 1112 } 1113} 1114``` 1115 1116### 使用@ComponentV2装饰器触发动态刷新 1117 1118在@ComponentV2装饰器装饰的自定义组件中配合@ObservedV2和@Trace装饰器,通过按值传递的方式可以实现UI刷新功能。 1119 1120【反例】 1121 1122在@ComponentV2装饰的自定义组件中,使用简单数据类型不可以触发UI的刷新。 1123 1124```ts 1125@ObservedV2 1126class ParamTmp { 1127 @Trace count : number = 0; 1128} 1129 1130@Builder 1131function renderNumber(paramNum: number) { 1132 Text(`paramNum : ${paramNum}`) 1133 .fontSize(30) 1134 .fontWeight(FontWeight.Bold) 1135} 1136 1137@Entry 1138@ComponentV2 1139struct PageBuilder { 1140 @Local class_value: ParamTmp = new ParamTmp(); 1141 // 此处使用简单数据类型不支持刷新UI的能力。 1142 @Local num_value: number = 0; 1143 private progressTimer: number = -1; 1144 1145 aboutToAppear(): void { 1146 this.progressTimer = setInterval(() => { 1147 if (this.class_value.count < 100) { 1148 this.class_value.count += 5; 1149 this.num_value += 5; 1150 } else { 1151 clearInterval(this.progressTimer); 1152 } 1153 }, 500); 1154 } 1155 1156 build() { 1157 Column() { 1158 renderNumber(this.num_value) 1159 } 1160 .width('100%') 1161 .height('100%') 1162 .padding(50) 1163 } 1164} 1165``` 1166 1167【正例】 1168 1169在@ComponentV2装饰器装饰的自定义组件中,只有使用@ObservedV2装饰的ParamTmp类和使用@Trace装饰的count属性才能触发UI刷新。 1170 1171```ts 1172@ObservedV2 1173class ParamTmp { 1174 @Trace count : number = 0; 1175} 1176 1177@Builder 1178function renderText(param: ParamTmp) { 1179 Column() { 1180 Text(`param : ${param.count}`) 1181 .fontSize(20) 1182 .fontWeight(FontWeight.Bold) 1183 } 1184} 1185 1186@Builder 1187function renderMap(paramMap: Map<string,number>) { 1188 Text(`paramMap : ${paramMap.get('name')}`) 1189 .fontSize(20) 1190 .fontWeight(FontWeight.Bold) 1191} 1192 1193@Builder 1194function renderSet(paramSet: Set<number>) { 1195 Text(`paramSet : ${paramSet.size}`) 1196 .fontSize(20) 1197 .fontWeight(FontWeight.Bold) 1198} 1199 1200@Builder 1201function renderNumberArr(paramNumArr: number[]) { 1202 Text(`paramNumArr : ${paramNumArr[0]}`) 1203 .fontSize(20) 1204 .fontWeight(FontWeight.Bold) 1205} 1206 1207@Entry 1208@ComponentV2 1209struct PageBuilder { 1210 @Local builderParams: ParamTmp = new ParamTmp(); 1211 @Local map_value: Map<string,number> = new Map(); 1212 @Local set_value: Set<number> = new Set([0]); 1213 @Local numArr_value: number[] = [0]; 1214 private progressTimer: number = -1; 1215 1216 aboutToAppear(): void { 1217 this.progressTimer = setInterval(() => { 1218 if (this.builderParams.count < 100) { 1219 this.builderParams.count += 5; 1220 this.map_value.set('name', this.builderParams.count); 1221 this.set_value.add(this.builderParams.count); 1222 this.numArr_value[0] = this.builderParams.count; 1223 } else { 1224 clearInterval(this.progressTimer); 1225 } 1226 }, 500); 1227 } 1228 1229 @Builder 1230 localBuilder() { 1231 Column() { 1232 Text(`localBuilder : ${this.builderParams.count}`) 1233 .fontSize(20) 1234 .fontWeight(FontWeight.Bold) 1235 } 1236 } 1237 1238 build() { 1239 Column() { 1240 this.localBuilder() 1241 Text(`builderParams :${this.builderParams.count}`) 1242 .fontSize(20) 1243 .fontWeight(FontWeight.Bold) 1244 renderText(this.builderParams) 1245 renderText({ count: this.builderParams.count }) 1246 renderMap(this.map_value) 1247 renderSet(this.set_value) 1248 renderNumberArr(this.numArr_value) 1249 } 1250 .width('100%') 1251 .height('100%') 1252 } 1253} 1254``` 1255 1256### 在\@Builder内创建自定义组件传递参数不刷新问题 1257 1258在parentBuilder函数中创建自定义组件HelloComponent,传递参数为class对象并修改对象内的值时,UI不会触发刷新功能。 1259 1260【反例】 1261 1262```ts 1263class Tmp { 1264 name: string = 'Hello'; 1265 age: number = 16; 1266} 1267 1268@Builder 1269function parentBuilder(params: Tmp) { 1270 Row() { 1271 Column() { 1272 Text(`parentBuilder===${params.name}===${params.age}`) 1273 .fontSize(20) 1274 .fontWeight(FontWeight.Bold) 1275 // 此写法不属于按引用传递方式,用法错误导致UI不刷新。 1276 HelloComponent({ info: params }) 1277 } 1278 } 1279} 1280 1281@Component 1282struct HelloComponent { 1283 @Prop info: Tmp = new Tmp(); 1284 1285 build() { 1286 Row() { 1287 Text(`HelloComponent===${this.info.name}===${this.info.age}`) 1288 .fontSize(20) 1289 .fontWeight(FontWeight.Bold) 1290 } 1291 } 1292} 1293 1294@Entry 1295@Component 1296struct ParentPage { 1297 @State nameValue: string = '张三'; 1298 @State ageValue: number = 18; 1299 1300 build() { 1301 Column() { 1302 parentBuilder({ name: this.nameValue, age: this.ageValue }) 1303 Button('Click me') 1304 .onClick(() => { 1305 // 此处修改内容时,不会引起HelloComponent处的变化 1306 this.nameValue = '李四'; 1307 this.ageValue = 20; 1308 }) 1309 } 1310 .height('100%') 1311 .width('100%') 1312 } 1313} 1314``` 1315 1316在parentBuilder函数中创建自定义组件HelloComponent,传递参数为对象字面量形式并修改对象内的值时,UI触发刷新功能。 1317 1318【正例】 1319 1320```ts 1321class Tmp { 1322 name: string = 'Hello'; 1323 age: number = 16; 1324} 1325 1326@Builder 1327function parentBuilder(params: Tmp) { 1328 Row() { 1329 Column() { 1330 Text(`parentBuilder===${params.name}===${params.age}`) 1331 .fontSize(20) 1332 .fontWeight(FontWeight.Bold) 1333 // 将整个对象拆分开变成简单类型,属于按引用传递方式,更改属性能够触发UI刷新。 1334 HelloComponent({ childName: params.name, childAge: params.age }) 1335 } 1336 } 1337} 1338 1339@Component 1340struct HelloComponent { 1341 @Prop childName: string = ''; 1342 @Prop childAge: number = 0; 1343 1344 build() { 1345 Row() { 1346 Text(`HelloComponent===${this.childName}===${this.childAge}`) 1347 .fontSize(20) 1348 .fontWeight(FontWeight.Bold) 1349 } 1350 } 1351} 1352 1353@Entry 1354@Component 1355struct ParentPage { 1356 @State nameValue: string = '张三'; 1357 @State ageValue: number = 18; 1358 1359 build() { 1360 Column() { 1361 parentBuilder({ name: this.nameValue, age: this.ageValue }) 1362 Button('Click me') 1363 .onClick(() => { 1364 // 此处修改内容时,会引起HelloComponent处的变化 1365 this.nameValue = '李四'; 1366 this.ageValue = 20; 1367 }) 1368 } 1369 .height('100%') 1370 .width('100%') 1371 } 1372} 1373``` 1374 1375### 在UI语句外调用\@Builder函数或方法影响节点正常刷新 1376 1377当\@Builder方法赋值给变量或者数组后,在UI方法中无法使用,且可能会造成刷新时节点显示异常。 1378 1379【反例】 1380```ts 1381@Entry 1382@Component 1383struct BackGround { 1384 @Builder 1385 myImages() { 1386 Column() { 1387 Image($r('app.media.startIcon')).width('100%').height('100%') 1388 } 1389 }; 1390 1391 @Builder 1392 myImages2() { 1393 Column() { 1394 Image($r('app.media.startIcon')).width('100%').height('100%') 1395 } 1396 }; 1397 1398 private Bg_list: Array<CustomBuilder> =[this.myImages(), this.myImages2()]; // 错误用法,应避免在UI方法外调用@Builder方法 1399 1400 @State bg_builder: CustomBuilder = this.myImages(); // 错误用法,应避免在UI方法外调用@Builder方法 1401 @State bg_Color: ResourceColor = Color.Orange; 1402 @State bg_Color2: ResourceColor = Color.Orange; 1403 @State index: number = 0; 1404 1405 build() { 1406 Column({space: 10}) { 1407 Text('1').width(100).height(50) 1408 Text('2').width(100).height(50) 1409 Text('3').width(100).height(50) 1410 1411 Text('4-1').width(100).height(50).fontColor(this.bg_Color) 1412 Text('5-1').width(100).height(50) 1413 Text('4-2').width(100).height(50) 1414 Text('5-2').width(100).height(50) 1415 Stack() { 1416 Column(){ 1417 Text('Vsync2') 1418 } 1419 .size({ width: '100%', height: '100%' }) 1420 .border({ width: 1, color: Color.Black }) 1421 } 1422 .size({ width: 100, height: 80 }) 1423 .backgroundColor('#ffbbd4bb') 1424 1425 Button('change').onClick((event: ClickEvent) => { 1426 this.index = 1; 1427 this.bg_Color = Color.Red; 1428 this.bg_Color2 = Color.Red; 1429 }) 1430 } 1431 .margin(10) 1432 } 1433} 1434``` 1435\@Builder方法赋值给变量或数组后在UI方法中无法使用,开发者应避免将\@Builder赋值给变量或数组后再使用。 1436 1437【正例】 1438```ts 1439@Entry 1440@Component 1441struct BackGround { 1442 @Builder 1443 myImages() { 1444 Column() { 1445 Image($r('app.media.startIcon')).width('100%').height('100%') 1446 } 1447 } 1448 1449 @Builder 1450 myImages2() { 1451 Column() { 1452 Image($r('app.media.startIcon')).width('100%').height('100%') 1453 } 1454 } 1455 1456 @State bg_Color: ResourceColor = Color.Orange; 1457 @State bg_Color2: ResourceColor = Color.Orange; 1458 @State index: number = 0; 1459 1460 build() { 1461 Column({ space: 10 }) { 1462 Text('1').width(100).height(50) 1463 Text('2').width(100).height(50).background(this.myImages) // 直接传递@Builder方法 1464 Text('3').width(100).height(50).background(this.myImages()) // 直接调用@Builder方法 1465 1466 Text('4-1').width(100).height(50).fontColor(this.bg_Color) 1467 Text('5-1').width(100).height(50) 1468 Text('4-2').width(100).height(50) 1469 Text('5-2').width(100).height(50) 1470 Stack() { 1471 Column() { 1472 Text('Vsync2') 1473 } 1474 .size({ width: '100%', height: '100%' }) 1475 .border({ width: 1, color: Color.Black }) 1476 } 1477 .size({ width: 100, height: 80 }) 1478 .backgroundColor('#ffbbd4bb') 1479 1480 Button('change').onClick((event: ClickEvent) => { 1481 this.index = 1; 1482 this.bg_Color = Color.Red; 1483 this.bg_Color2 = Color.Red; 1484 }) 1485 } 1486 .margin(10) 1487 } 1488} 1489``` 1490 1491### 在\@Builder方法中使用MutableBinding未传递set访问器 1492 1493\@Builder方法定义时使用MutableBinding,构造时没有给MutableBinding类型参数传递set访问器,触发set访问器会造成运行时错误。 1494 1495【反例】 1496```ts 1497import { UIUtils, Binding, MutableBinding } from '@kit.ArkUI'; 1498@ObservedV2 1499class GlobalTmp { 1500 @Trace str_value: string = 'Hello'; 1501} 1502 1503@Builder 1504function builderWithTwoParams(param1: Binding<GlobalTmp>, param2: MutableBinding<number>) { 1505 Column() { 1506 Text(`str_value: ${param1.value.str_value}`) 1507 Button(`num: ${param2.value}`) 1508 .onClick(()=>{ 1509 param2.value += 1; // 点击Button触发set访问器会造成运行时错误 1510 }) 1511 }.borderWidth(1) 1512} 1513 1514@Entry 1515@ComponentV2 1516struct MakeBindingTest { 1517 @Local globalTmp: GlobalTmp = new GlobalTmp(); 1518 @Local num: number = 0; 1519 1520 build() { 1521 Column() { 1522 Text(`${this.globalTmp.str_value}`) 1523 builderWithTwoParams(UIUtils.makeBinding(() => this.globalTmp), 1524 UIUtils.makeBinding<number>(() => this.num)) // 构造MutableBinding类型参数时没有传SetterCallback 1525 Button('点击改变参数值').onClick(() => { 1526 this.globalTmp.str_value = 'Hello World 2025'; 1527 this.num = 1; 1528 }) 1529 } 1530 } 1531} 1532``` 1533MutableBinding的使用规格详见[状态管理API文档](../../reference/apis-arkui/js-apis-StateManagement.md#mutablebindingt20)。 1534 1535【正例】 1536```ts 1537import { UIUtils, Binding, MutableBinding } from '@kit.ArkUI'; 1538 1539@ObservedV2 1540class GlobalTmp { 1541 @Trace str_value: string = 'Hello'; 1542} 1543 1544@Builder 1545function builderWithTwoParams(param1: Binding<GlobalTmp>, param2: MutableBinding<number>) { 1546 Column() { 1547 Text(`str_value: ${param1.value.str_value}`) 1548 Button(`num: ${param2.value}`) 1549 .onClick(() => { 1550 param2.value += 1; // 修改了MutableBinding类型参数的value属性 1551 }) 1552 }.borderWidth(1) 1553} 1554 1555@Entry 1556@ComponentV2 1557struct MakeBindingTest { 1558 @Local globalTmp: GlobalTmp = new GlobalTmp(); 1559 @Local num: number = 0; 1560 1561 build() { 1562 Column() { 1563 Text(`${this.globalTmp.str_value}`) 1564 builderWithTwoParams(UIUtils.makeBinding(() => this.globalTmp), 1565 UIUtils.makeBinding<number>(() => this.num, 1566 val => { 1567 this.num = val; 1568 })) 1569 Button('点击改变参数值').onClick(() => { 1570 this.globalTmp.str_value = 'Hello World 2025'; 1571 this.num = 1; 1572 }) 1573 } 1574 } 1575} 1576``` 1577 1578### 在\@Builder装饰的函数内部修改入参内容 1579 1580不使用[MutableBinding](../../reference/apis-arkui/js-apis-StateManagement.md#mutablebindingt20)的情况下,在\@Builder装饰的函数内部修改参数值,修改不会生效且可能造成运行时错误。 1581 1582【反例】 1583```ts 1584@Builder 1585function MyGlobalBuilder(value: string) { 1586 Column() { 1587 Text(`MyGlobalBuilder: ${value} `) 1588 .fontSize(16) 1589 .onClick(() => { 1590 // 简单类型按值传递的@Builder函数中修改参数,不闪退但UI不刷新 1591 value = 'value change'; 1592 }) 1593 }.borderWidth(1) 1594} 1595 1596interface Temp { 1597 paramA: string; 1598} 1599 1600@Builder 1601function overBuilder(param: Temp) { 1602 Row() { 1603 Column() { 1604 Button(`overBuilder === ${param.paramA}`) 1605 .onClick(() => { 1606 // 错误写法,不允许在@Builder装饰的函数内部修改对象类型参数的属性,闪退且UI不刷新 1607 param.paramA = 'Yes'; 1608 }) 1609 Button('change') 1610 .onClick(() => { 1611 // 错误写法,不允许在@Builder装饰的函数内部修改对象类型参数的引用,不闪退但UI不刷新 1612 param = { paramA: 'change trial' }; 1613 }) 1614 } 1615 } 1616} 1617 1618@Entry 1619@Component 1620struct Parent { 1621 @State label: string = 'Hello'; 1622 @State message1: string = 'Value Passing'; 1623 1624 @Builder 1625 extendBlank() { 1626 Row() { 1627 Blank() 1628 } 1629 .height(20) 1630 } 1631 1632 build() { 1633 Column() { 1634 // 按引用传递能实现参数变化时的UI刷新,但不能在@Builder函数内部修改参数 1635 overBuilder({ paramA: this.label }); 1636 this.extendBlank(); 1637 Button('click me') 1638 .onClick(() => { 1639 this.label = 'ArkUI'; 1640 }) 1641 this.extendBlank(); 1642 MyGlobalBuilder(this.message1); 1643 } 1644 } 1645} 1646``` 1647正确使用[MutableBinding](../../reference/apis-arkui/js-apis-StateManagement.md#mutablebindingt20)可以帮助开发者在\@Builder装饰的函数内部修改参数值。 1648 1649【正例】 1650```ts 1651import { UIUtils, MutableBinding } from '@kit.ArkUI'; 1652 1653// 使用MutableBinding在@Builder装饰的函数中修改参数值 1654@Builder 1655function MyGlobalBuilderMod(str: MutableBinding<string>) { 1656 Column() { 1657 Text(`Mod--MyGlobalBuilder: ${str.value}`) 1658 .fontSize(16) 1659 .onClick(() => { 1660 str.value = 'value change mod'; 1661 }) 1662 } 1663} 1664 1665interface Temp { 1666 paramA: string; 1667} 1668 1669// 使用MutableBinding在@Builder装饰的函数内部修改参数值 1670@Builder 1671function overBuilderMod(param: MutableBinding<Temp>) { 1672 Column() { 1673 Button(`Mod--overBuilder === ${param.value.paramA}`) 1674 .onClick(() => { 1675 param.value.paramA = 'Yes'; 1676 }) 1677 Button(`change`) 1678 .onClick(() => { 1679 param.value = { paramA: 'trialOne' }; 1680 }) 1681 } 1682} 1683 1684@Entry 1685@Component 1686struct Parent { 1687 @State label: string = 'Hello'; 1688 @State message1: string = 'Value Passing'; 1689 @State objectOne: Temp = { 1690 paramA: this.label 1691 }; 1692 1693 @Builder 1694 extendBlank() { 1695 Row() { 1696 Blank() 1697 } 1698 .height(20) 1699 } 1700 1701 build() { 1702 Column() { 1703 // 使用MutableBinding时无法传对象字面量,需要先将字面量对象抽出为状态变量 1704 overBuilderMod( 1705 UIUtils.makeBinding<Temp>( 1706 () => this.objectOne, 1707 value => { 1708 this.objectOne = value; // 必须要传SetterCallback,否则触发时会造成运行时错误 1709 } 1710 ) 1711 ) 1712 this.extendBlank(); 1713 Button('click me') 1714 .onClick(() => { 1715 this.objectOne.paramA = 'ArkUI'; 1716 }) 1717 this.extendBlank(); 1718 MyGlobalBuilderMod( 1719 UIUtils.makeBinding<string>( 1720 () => this.message1, 1721 value => { 1722 this.message1 = value; // 必须要传SetterCallback,否则触发时会造成运行时错误 1723 } 1724 ) 1725 ); 1726 } 1727 } 1728} 1729``` 1730 1731### 在\@Watch函数中执行\@Builder函数 1732 1733在\@Watch函数中执行\@Builder函数,可能导致UI刷新异常。 1734 1735【反例】 1736```ts 1737@Entry 1738@Component 1739struct Child { 1740 @Provide @Watch('provideWatch') content: string = 'Index: hello world'; 1741 1742 @Builder 1743 watchBuilder(content: string) { 1744 Row() { 1745 Text(`${content}`) 1746 } 1747 } 1748 1749 provideWatch() { 1750 this.watchBuilder(this.content); // 错误写法,在@Watch函数中使用@Builder函数 1751 } 1752 1753 build() { 1754 Column() { 1755 Button(`content value: ${this.content}`) 1756 .onClick(() => { 1757 this.content += '_world'; 1758 }) 1759 this.watchBuilder(this.content); 1760 } 1761 } 1762} 1763``` 1764Button按钮会出现UI异常的情况,开发者需要避免在\@Watch函数中使用\@Builder函数。 1765 1766【正例】 1767```ts 1768@Entry 1769@Component 1770struct Child { 1771 @Provide @Watch('provideWatch') content: string = 'Index: hello world'; 1772 1773 @Builder 1774 watchBuilder(content: string) { 1775 Row() { 1776 Text(`${content}`) 1777 } 1778 } 1779 1780 provideWatch() { 1781 console.info(`content value has changed.`); 1782 } 1783 1784 build() { 1785 Column() { 1786 Button(`content value: ${this.content}`) 1787 .onClick(() => { 1788 this.content += '_world'; 1789 }) 1790 this.watchBuilder(this.content); 1791 } 1792 } 1793} 1794``` 1795