• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  ![builderparam-demo1](figures/builderparam-demo1.png)
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 ![builderparam-demo2](figures/builderparam-demo2.png)
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![builderparam-demo3](figures/builderparam-demo3.png)
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![builderparam-demo4](figures/builderparam-demo4.png)
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![builderparam-demo5](figures/builderparam-demo5.png)
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![builderparam-demo6](figures/builderparam-demo6.png)
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```