• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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![arkts-builder-usage-scenario1](figures/arkts-builder-usage-scenario1.gif)
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![arkts-builder-usage-scenario2](figures/arkts-builder-usage-scenario2.gif)
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![arkts-builder-usage-scenario3](figures/arkts-builder-usage-scenario3.gif)
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![arkts-builder-usage-scenario4](figures/arkts-builder-usage-scenario4.gif)
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![arkts-builder-usage-scenario5](figures/arkts-builder-usage-scenario5.gif)
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![arkts-builder-usage-scenario6](figures/arkts-builder-usage-scenario6.gif)
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![arkts-builder-usage-scenario8](figures/arkts-builder-usage-scenario8.gif)
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![arkts-builder-usage-scenario7](figures/arkts-builder-usage-scenario7.gif)
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![arkts-builder-refresh](figures/arkts-builder-refresh.gif)
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