This content originally appeared on DEV Community and was authored by 程序员一鸣
Foreword
the code case of this article is based on Api13. Warm reminder: The content is relatively simple. If you have mastered it, you can skip it.
If there are many components in a page, we all write them into the build function. Obviously, the build function code will be very redundant, and when there is the same UI, it will not be reusable. Then, how to solve this problem? The answer is to extract it. In fact, it is very common in actual development to realize the extraction and stripping of UI components within the page, that is, through @ Builder decorator to achieve.
Simple Case
the following is a multi-Text display case, which is very simple, that is, several Text components are written in the build function before extraction.
@Entry
@Component
struct Index {
build() {
Column() {
Text("test 1")
.fontSize(20)
.fontWeight(FontWeight.Bold)
Text("test 2")
.fontSize(20)
.fontWeight(FontWeight.Bold)
Text("test 3")
.fontSize(20)
.fontWeight(FontWeight.Bold)
Text("test 4")
.fontSize(20)
.fontWeight(FontWeight.Bold)
}
.height('100%')
.width('100%')
}
}
Use @ Builder decorator after:
@Entry
@Component
struct Index {
@Builder
textView(text: string) {
Text(text)
.fontSize(20)
.fontWeight(FontWeight.Bold)
}
build() {
Column() {
this.textView("test 1")
this.textView("test 2")
this.textView("test 3")
this.textView("test 4")
}
.height('100%')
.width('100%')
}
}
obviously, the code is much simpler than before it was extracted. Although the above is only a case, in actual development, the complexity of the page is far more complicated than that of the case, so it should be used reasonably. @ Builder decorator .
What is @ Builder?
It has been clearly informed in the foreword, @builder it is a decorator, mainly used UI element reuse and extraction, @builderThe functions modified, collectively referred to as” custom Build Functions “, any UI component can be defined in the function, and the usage is the same as that in build.
Mode of use
the @ Builder decorator can be used in two ways. One is defined in the UI component, which is the above-mentioned way of use. It can be called a private custom build function, that is, it can only be used for the current UI component, and other UI components cannot be used.
The global custom build function corresponds to the private custom build function, which can be used by any UI component. function keyword, if it is defined in a non-UI component, you need to use the export keyword to export.
We can customize an extension class and define the common components here.
@Builder
export function TextView(text: string) {
Text(text)
.fontSize(20)
.fontWeight(FontWeight.Bold)
}
Can be used in any page or UI component.
import { TextView } from './BuilderView'
@Entry
@Component
struct Index {
build() {
Column() {
TextView("test 1")
TextView("test 2")
TextView("test 3")
TextView("test 3")
}
.height('100%')
.width('100%')
}
}
Of course, if it is not global sharing, but only sharing within this page, it can also be implemented in this way.
Data Update
when we directly change the passed variables, we will find that there is no data change in the function decorated by @ Builder. In the following case, no change will occur when clicking Button.
@Entry
@Component
struct Index {
@State testContent: string = "test"
@Builder
textView(text: string) {
Text(text)
.fontSize(20)
.fontWeight(FontWeight.Bold)
}
build() {
Column() {
this.textView(this.testContent)
Button("change")
.margin({ top: 10 })
.onClick(() => {
this.testContent = "test 2"
})
}
.height('100%')
.width('100%')
.justifyContent(FlexAlign.Center)
}
}
This is because, inside the function decorated by @ Builder, it is not allowed to change the parameter value, that is the change of state variable will not cause UI Refresh in @ Builder method, so how can it be changed dynamically?There are two ways to pass the data in the function modified by @ Builder. One is to directly pass the current reference, that is, the current class, and call it directly. The other is to pass it by reference.
this points to the current class
make the following changes to the above code. By passing the value, the current class, that is, the current this, is directly called by this.
@Entry
@Component
struct Index {
@State testContent: string = "test"
@Builder
textView(_this: Index) {
Text(_this.testContent)
.fontSize(20)
.fontWeight(FontWeight.Bold)
}
build() {
Column() {
this.textView(this)
Button("change")
.margin({ top: 10 })
.onClick(() => {
this.testContent = "test 2"
})
}
.height('100%')
.width('100%')
.justifyContent(FlexAlign.Center)
}
}
It can be found that after clicking Button, the data has changed dynamically.
Passing Parameters by Reference
passing the current this directly can be said to be the simplest way. In addition to this way, we can also use reference to pass parameters to dynamically change data, that is, by passing objects.
class TestBean {
testContent?: string;
}
@Entry
@Component
struct Index {
@State testContent: string = "test"
@Builder
textView(testBean: TestBean) {
Text(testBean.testContent)
.fontSize(20)
.fontWeight(FontWeight.Bold)
}
build() {
Column() {
this.textView({ testContent: this.testContent })
Button("change")
.margin({ top: 10 })
.onClick(() => {
this.testContent = "test 2"
})
}
.height('100%')
.width('100%')
.justifyContent(FlexAlign.Center)
}
}
It can be seen that the above data transfer can also realize dynamic change of data. Compared with this transfer, this method is more flexible. After all, in actual development, we may encounter the situation that multiple pages share components. this transfer is obviously only suitable for the current page. If multiple pages are reused, it is better to transfer in the form of objects.
Thus, in use @ Builder when passing parameters, if you want the UI Refresh in the @ Builder method can be implemented by passing parameters by reference. UI cannot be updated by passing by value.
Parameter Rules
for functions decorated with @ Builder, the defined parameter types must be consistent with the passed types, and undefined, null, and expressions that return undefined and null are not allowed.
It is also very important that @ Builder will not trigger dynamic rendering UI if two or more parameters are passed in. For example, in the above case, we randomly add one parameter.
@Builder
textView(testBean: TestBean, isBoolean: boolean) {
Text(testBean.testContent)
.fontSize(20)
.fontWeight(FontWeight.Bold)
}
After running, it can be found that the data cannot be changed dynamically. How can multiple values be passed? There are two ways. The first is to use this as the passing object and use this to call more defined parameters. The second is to define it directly in the object. Since they are all passed in the form of objects, why not define it directly in the object?
@ ComponentV2 decorator update
using the ComponentV2 decorator follows the same principle, which is using simple data types cannot trigger a refresh of the UI.
@Entry
@ComponentV2
struct Index {
@Local testContent: string = "test"
@Builder
textView(testContent: string) {
Column() {
Text(testContent)
.fontSize(20)
.fontWeight(FontWeight.Bold)
}
}
build() {
Column() {
this.textView(this.testContent)
Button("change")
.margin({ top: 10 })
.onClick(() => {
this.testContent = "test 2"
})
}
.height('100%')
.width('100%')
.justifyContent(FlexAlign.Center)
}
}
If you want to update the UI, you still need to pass parameters by reference, that is, change to object passing.
class TestBean {
testContent?: string;
}
@Entry
@ComponentV2
struct Index {
@Local testContent: string = "test"
@Builder
textView(testBean: TestBean) {
Column() {
Text(testBean.testContent)
.fontSize(20)
.fontWeight(FontWeight.Bold)
}
}
build() {
Column() {
this.textView({ testContent: this.testContent })
Button("change")
.margin({ top: 10 })
.onClick(() => {
this.testContent = "test 2"
})
}
.height('100%')
.width('100%')
.justifyContent(FlexAlign.Center)
}
}
If you pass it directly as an object, without the help of member variables, you need use the @ ObservedV2 decorated object class and the @ Trace decorated property to trigger a UI Refresh.
@ObservedV2
class TestBean {
@Trace testContent: string = "test";
}
@Entry
@ComponentV2
struct Index {
@Local testBean: TestBean = new TestBean()
@Builder
textView(testBean: TestBean) {
Column() {
Text(testBean.testContent)
.fontSize(20)
.fontWeight(FontWeight.Bold)
}
}
build() {
Column() {
this.textView(this.testBean)
Button("change")
.margin({ top: 10 })
.onClick(() => {
this.testBean.testContent = "test 2"
})
}
.height('100%')
.width('100%')
.justifyContent(FlexAlign.Center)
}
}
Related Summary
@ Builder decoration is a very important decorator in the development of Hongmeng UI. In actual development, reasonable and correct use can make our code more concise. There are two points to note. First, whether to use private or global depends on the current component reuse mechanism. If multiple pages are used, it is recommended to focus on the global situation. The second is the dynamic update of parameters, if there is an update, it is passed by reference parameter; If there is no update, it is passed by value.
This content originally appeared on DEV Community and was authored by 程序员一鸣