sroxck

sroxck

ArkTs 元件基礎

在 ArkUI 中,UI 顯示的內容均為組件,由框架直接提供的稱為系統組件,由開發者定義的稱為自定義組件。在進行 UI 界面開發時,通常不是簡單的將系統組件進行組合使用,而是需要考慮代碼可重用性、業務邏輯與 UI 分離,後續版本演進等因素。因此,將 UI 和部分業務邏輯封裝成自定義組件是不可或缺的能力。

自定義組件#

自定義組件具有以下特點:

  • 可組合:允許開發者組合使用系統組件、及其屬性和方法。
  • 可重用:自定義組件可以被其他組件重用,並作為不同的實例在不同的父組件或容器中使用。
  • 數據驅動 UI 更新:通過狀態變量的改變,來驅動 UI 的刷新。

自定義組件的基本用法#

@Component
struct HelloComponent {
  @State message: string = 'Hello, World!';

  build() {
    // HelloComponent自定義組件組合系統組件Row和Text
    Row() {
      Text(this.message)
        .onClick(() => {
          // 狀態變量message的改變驅動UI刷新,UI從'Hello, World!'刷新為'Hello, ArkUI!'
          this.message = 'Hello, ArkUI!';
        })
    }
  }
}

::: warning 注意

如果在另外的文件中引用該自定義組件,需要使用 export 關鍵字導出,並在使用的頁面 import 該自定義組件。
:::

HelloComponent 可以在其他自定義組件中的 build () 函數中多次創建,實現自定義組件的重用。

class HelloComponentParam {
  message: string = ""
}

@Entry
@Component
struct ParentComponent {
  param: HelloComponentParam = {
    message: 'Hello, World!'
  }

  build() {
    Column() {
      Text('ArkUI message')
      HelloComponent(this.param);
      Divider()
      HelloComponent(this.param);
    }
  }
}

自定義組件的基本結構#

  • struct:自定義組件基於 struct 實現,struct + 自定義組件名 + {...} 的組合構成自定義組件,不能有繼承關係。對於 struct 的實例化,可以省略 new。

::: tip 說明
自定義組件名、類名、函數名不能和系統組件名相同。
:::

  • @Component@Component 裝飾器僅能裝飾 struct 關鍵字聲明的數據結構。struct 被 @Component 裝飾後具備組件化的能力,需要實現 build 方法描述 UI,一個 struct 只能被一個 @Component 裝飾。@Component 可以接受一個可選的 bool 類型參數。

::: tip 說明
從 API version 9 開始,該裝飾器支持在 ArkTS 卡片中使用。

從 API version 11 開始,@Component 可以接受一個可選的 bool 類型參數。
:::

@Component
struct MyComponent {
}

freezeWhenInactive#

組件凍結選項。

@Component({ freezeWhenInactive: true })
struct MyComponent {
}

build () 函數#

build () 函數用於定義自定義組件的聲明式 UI 描述,自定義組件必須定義 build () 函數。

@Component
struct MyComponent {
  build() {
  }
}

@Entry#

@Entry 裝飾的自定義組件將作為 UI 頁面的入口。在單個 UI 頁面中,最多可以使用 @Entry 裝飾一個自定義組件。@Entry 可以接受一個可選的 LocalStorage 的參數

::: tip 說明
從 API version 9 開始,該裝飾器支持在 ArkTS 卡片中使用。

從 API version 10 開始,@Entry 可以接受一個可選的 LocalStorage 的參數或者一個可選的 EntryOptions 參數。

從 API version 11 開始,該裝飾器支持在元服務中使用。
:::

@Entry
@Component
struct MyComponent {
}```

EntryOptions10#

命名路由跳轉選項。

@Entry({ routeName : 'myPage' })
@Component
struct MyComponent {
}

@Reusable@Reusable 裝飾的自定義組件具備可重用能力

成員函數 / 變量#

  • 自定義組件除了必須要實現 build () 函數外,還可以實現其他成員函數,成員函數具有以下約束:
  • 自定義組件的成員函數為私有的,且不建議聲明成靜態函數。
  • 自定義組件可以包含成員變量,成員變量具有以下約束:
  • 自定義組件的成員變量為私有的,且不建議聲明成靜態變量。
  • 自定義組件的成員變量本地初始化有些是可選的,有些是必選的。具體是否需要本地初始化,是否需要從父組件通過參數傳遞初始化子組件的成員變量,請參考狀態管理。

自定義組件的參數規定#

從上文的示例中,我們已經了解到,可以在 build 方法裡創建自定義組件,在創建自定義組件的過程中,根據裝飾器的規則來初始化自定義組件的參數。

@Component
struct MyComponent {
  private countDownFrom: number = 0;
  private color: Color = Color.Blue;

  build() {
  }
}

@Entry
@Component
struct ParentComponent {
  private someColor: Color = Color.Pink;

  build() {
    Column() {
      // 創建MyComponent實例,並將創建MyComponent成員變量countDownFrom初始化為10,將成員變量color初始化為this.someColor
      MyComponent({ countDownFrom: 10, color: this.someColor })
    }
  }
}

build () 函數#

所有聲明在 build () 函數的語句,我們統稱為 UI 描述,UI 描述需要遵循以下規則:

  • @Entry 裝飾的自定義組件,其 build () 函數下的根節點唯一且必要,且必須為容器組件,其中 ForEach 禁止作為根節點。
  • @Component 裝飾的自定義組件,其 build () 函數下的根節點唯一且必要,可以為非容器組件,其中 ForEach 禁止作為根節點。
@Entry
@Component
struct MyComponent {
  build() {
    // 根節點唯一且必要,必須為容器組件
    Row() {
      ChildComponent()
    }
  }
}

@Component
struct ChildComponent {
  build() {
    // 根節點唯一且必要,可為非容器組件
    Image('test.jpg')
  }
}
  • 不允許聲明本地變量,反例如下
build() {
  // 反例:不允許聲明本地變量
  let a: number = 1;
}
  • 不允許在 UI 描述裡直接使用 console.info,但允許在方法或者函數裡使用,反例如下。
build() {
  // 反例:不允許console.info
  console.info('print debug log');
}
  • 不允許調用沒有用 @Builder 裝飾的方法,允許系統組件的參數是 TS 方法的返回值。
@Component
struct ParentComponent {
  doSomeCalculations() {
  }

  calcTextValue(): string {
    return 'Hello World';
  }

  @Builder doSomeRender() {
    Text(`Hello World`)
  }

  build() {
    Column() {
      // 反例:不能調用沒有用@Builder裝飾的方法
      this.doSomeCalculations();
      // 正例:可以調用
      this.doSomeRender();
      // 正例:參數可以為調用TS方法的返回值
      Text(this.calcTextValue())
    }
  }
}
  • 不允許使用 switch 語法,如果需要使用條件判斷,請使用 if。示例如下。
build() {
  Column() {
    // 反例:不允許使用switch語法
    switch (expression) {
      case 1:
        Text('...')
        break;
      case 2:
        Image('...')
        break;
      default:
        Text('...')
        break;
    }
    // 正例:使用if
    if(expression == 1) {
      Text('...')
    } else if(expression == 2) {
      Image('...')
    } else {
      Text('...')
    }
  }
}

  • 不允許直接改變狀態變量,反例如下。詳細分析見 @State 常見問題:不允許在 build 裡改狀態變量
@Component
struct CompA {
  @State col1: Color = Color.Yellow;
  @State col2: Color = Color.Green;
  @State count: number = 1;
  build() {
    Column() {
      // 應避免直接在Text組件內改變count的值
      Text(`${this.count++}`)
        .width(50)
        .height(50)
        .fontColor(this.col1)
        .onClick(() => {
          this.col2 = Color.Red;
        })
      Button("change col1").onClick(() =>{
        this.col1 = Color.Pink;
      })
    }
    .backgroundColor(this.col2)
  }
}

在 ArkUI 狀態管理中,狀態驅動 UI 更新。

所以,不能在自定義組件的 build () 或 @Builder 方法裡直接改變狀態變量,這可能會造成循環渲染的風險。Text ('${this.count++}') 在全量更新或最小化更新會產生不同的影響:

  • 全量更新(API8 及以前版本): ArkUI 可能會陷入一個無限的重渲染的循環裡,因為 Text 組件的每一次渲染都會改變應用的狀態,就會再引起下一輪渲染的開啟。 當 this.col2 更改時,都会執行整個 build 構建函數,因此,Text(${this.count++})綁定的文本也會更改,每次重新渲染Text(${this.count++}),又會使 this.count 狀態變量更新,導致新一輪的 build 執行,從而陷入無限循環。

  • 最小化更新(API9 - 至今版本): 當 this.col2 更改時,只有 Column 組件會更新,Text 組件不會更改。 只當 this.col1 更改時,會去更新整個 Text 組件,其所有屬性函數都會執行,所以會看到 Text(${this.count++})自增。因為目前 UI 以組件為單位進行更新,如果組件上某一個屬性發生改變,會更新整體的組件。所以整體的更新鏈路是:this.col1 = Color.Pink -> Text 組件整體更新 ->this.count++ ->Text 組件整體更新。值得注意的是,這種寫法在初次渲染時會導致 Text 組件渲染兩次,從而對性能產生影響。

build 函數中更改應用狀態的行為可能會比上面的示例更加隱蔽,比如:

  • @Builder@Extend@Styles 方法內改變狀態變量 。
  • 計算參數時調用函數中改變應用狀態變量,例如 Text('${this.calcLabel()}')
  • 對當前數組做出修改,sort () 改變了數組 this.arr,隨後的 filter 方法會返回一個新的數組。

自定義組件通用樣式#

自定義組件通過 “.” 鏈式調用的形式設置通用樣式。

@Component
struct MyComponent2 {
  build() {
    Button(`Hello World`)
  }
}

@Entry
@Component
struct MyComponent {
  build() {
    Row() {
      MyComponent2()
        .width(200)
        .height(300)
        .backgroundColor(Color.Red)
    }
  }
}

::: tip 說明
ArkUI 給自定義組件設置樣式時,相當於給 MyComponent2 套了一個不可見的容器組件,而這些樣式是設置在容器組件上的,而非直接設置給 MyComponent2 的 Button 組件。通過渲染結果我們可以很清楚的看到,背景顏色紅色並沒有直接生效在 Button 上,而是生效在 Button 所處的開發者不可見的容器組件上。
:::

此文由 Mix Space 同步更新至 xLog
原始鏈接為 http://www.sroxck.top/posts/harmony/arkts-components


載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。