開始する前に、カスタムコンポーネントとページの関係を明確にしましょう:
- カスタムコンポーネント:@Component デコレーターで装飾された UI ユニットで、複数のシステムコンポーネントを組み合わせて UI の再利用を実現し、コンポーネントのライフサイクルを呼び出すことができます。
- ページ:アプリケーションの UI ページです。1 つまたは複数のカスタムコンポーネントで構成され、@Entry デコレーターで装飾されたカスタムコンポーネントがページのエントリコンポーネント、つまりページのルートノードです。1 つのページには、@Entry が 1 つだけ存在することができます。@Entry で装飾されたコンポーネントのみがページのライフサイクルを呼び出すことができます。
ページライフサイクル、つまり @Entry で装飾されたコンポーネントのライフサイクルは、以下のライフサイクルインターフェースを提供します:
- onPageShow:ページが表示されるたびにトリガーされます。ルーティングプロセスやアプリケーションがフォアグラウンドに入るなどのシーンを含みます。
- onPageHide:ページが非表示になるたびにトリガーされます。ルーティングプロセスやアプリケーションがバックグラウンドに入るなどのシーンを含みます。
- onBackPress:ユーザーが戻るボタンをクリックしたときにトリガーされます。
コンポーネントライフサイクル、つまり一般的に @Component で装飾されたカスタムコンポーネントのライフサイクルは、以下のライフサイクルインターフェースを提供します:
- aboutToAppear:コンポーネントが表示される直前にこのインターフェースがコールバックされます。具体的なタイミングは、カスタムコンポーネントの新しいインスタンスを作成した後、build () 関数を実行する前に実行されます。
- onDidBuild:コンポーネントの build () 関数が実行された後にこのインターフェースがコールバックされます。onDidBuild 関数内で状態変数を変更したり、animateTo などの機能を使用することは推奨されません。これにより不安定な UI 表現が引き起こされる可能性があります。
- aboutToDisappear:aboutToDisappear 関数は、カスタムコンポーネントが破棄される前に実行されます。aboutToDisappear 関数内で状態変数を変更することは許可されておらず、特に @Link 変数の変更はアプリケーションの動作を不安定にする可能性があります。
カスタムコンポーネントの作成とレンダリングプロセス#
- カスタムコンポーネントの作成:カスタムコンポーネントのインスタンスは ArkUI フレームワークによって作成されます。
- カスタムコンポーネントのメンバー変数の初期化:ローカルのデフォルト値またはコンストラクタを通じてパラメータを渡してカスタムコンポーネントのメンバー変数を初期化します。初期化の順序はメンバー変数の定義順序です。
- 開発者が aboutToAppear を定義した場合、aboutToAppear メソッドが実行されます。
- 初回レンダリング時に、build メソッドがシステムコンポーネントをレンダリングし、子コンポーネントがカスタムコンポーネントである場合はカスタムコンポーネントのインスタンスが作成されます。初回レンダリングの過程で、フレームワークは状態変数とコンポーネントのマッピング関係を記録し、状態変数が変更されると関連するコンポーネントを更新します。
- 開発者が onDidBuild を定義した場合、onDidBuild メソッドが実行されます。
カスタムコンポーネントの再レンダリング#
イベントハンドラがトリガーされ(例えば、クリックイベントが設定され、クリックイベントがトリガーされる)、状態変数が変更された場合、または LocalStorage / AppStorage の属性が変更され、バインドされた状態変数の値が変更された場合:
- フレームワークは変化を観察し、再レンダリングを開始します。
- フレームワークが保持する 2 つのマップ(カスタムコンポーネントの作成とレンダリングプロセスの第 4 ステップ)に基づいて、フレームワークはその状態変数が管理している UI コンポーネントと、それらの UI コンポーネントに対応する更新関数を知ることができます。これらの UI コンポーネントの更新関数を実行し、最小限の更新を実現します。
カスタムコンポーネントの削除#
if コンポーネントの分岐が変更された場合、または ForEach ループレンダリング中の配列の数が変更された場合、コンポーネントは削除されます:
- コンポーネントを削除する前に、その aboutToDisappear ライフサイクル関数が呼び出され、このノードが破棄されることを示します。ArkUI のノード削除メカニズムは、バックエンドノードがコンポーネントツリーから直接取り外され、バックエンドノードが破棄され、フロントエンドノードが解参照され、フロントエンドノードに参照がなくなったときに、JS 仮想マシンのガベージコレクションによって削除されます。
- カスタムコンポーネントとその変数は削除され、同期変数(例えば @Link、@Prop、@StorageLink)がある場合は、同期ソースから登録が解除されます。
ライフサイクル aboutToDisappear 内で async await を使用することは推奨されません。ライフサイクルの aboutToDisappear で非同期操作(Promise またはコールバックメソッド)を使用すると、カスタムコンポーネントは Promise のクロージャ内に保持され、コールバックメソッドが実行されるまで保持されます。この動作はカスタムコンポーネントのガベージコレクションを妨げます。
以下の例は、ライフサイクルの呼び出しタイミングを示しています:
// Index.ets
import { router } from '@kit.ArkUI';
@Entry
@Component
struct MyComponent {
@State showChild: boolean = true;
@State btnColor:string = "#FF007DFF";
// 只有被@Entry装饰的组件才可以调用页面的生命周期
onPageShow() {
console.info('Index onPageShow');
}
// 只有被@Entry装饰的组件才可以调用页面的生命周期
onPageHide() {
console.info('Index onPageHide');
}
// 只有被@Entry装饰的组件才可以调用页面的生命周期
onBackPress() {
console.info('Index onBackPress');
this.btnColor ="#FFEE0606";
return true // 返回true表示页面自己处理返回逻辑,不进行页面路由;返回false表示使用默认的路由返回逻辑,不设置返回值按照false处理
}
// 组件生命周期
aboutToAppear() {
console.info('MyComponent aboutToAppear');
}
// 组件生命周期
onDidBuild() {
console.info('MyComponent onDidBuild');
}
// 组件生命周期
aboutToDisappear() {
console.info('MyComponent aboutToDisappear');
}
build() {
Column() {
// this.showChild为true,创建Child子组件,执行Child aboutToAppear
if (this.showChild) {
Child()
}
// this.showChild为false,删除Child子组件,执行Child aboutToDisappear
Button('delete Child')
.margin(20)
.backgroundColor(this.btnColor)
.onClick(() => {
this.showChild = false;
})
// push到page页面,执行onPageHide
Button('push to next page')
.onClick(() => {
router.pushUrl({ url: 'pages/page' });
})
}
}
}
@Component
struct Child {
@State title: string = 'Hello World';
// 组件生命周期
aboutToDisappear() {
console.info('[lifeCycle] Child aboutToDisappear')
}
// 组件生命周期
onDidBuild() {
console.info('[lifeCycle] Child onDidBuild');
}
// 组件生命周期
aboutToAppear() {
console.info('[lifeCycle] Child aboutToAppear')
}
build() {
Text(this.title)
.fontSize(50)
.margin(20)
.onClick(() => {
this.title = 'Hello ArkUI';
})
}
}
// page.ets
@Entry
@Component
struct page {
@State textColor: Color = Color.Black;
@State num: number = 0;
onPageShow() {
this.num = 5;
}
onPageHide() {
console.log("page onPageHide");
}
onBackPress() { // 不设置返回值按照false处理
this.textColor = Color.Grey;
this.num = 0;
}
aboutToAppear() {
this.textColor = Color.Blue;
}
build() {
Column() {
Text(`num の値は:${this.num}`)
.fontSize(30)
.fontWeight(FontWeight.Bold)
.fontColor(this.textColor)
.margin(20)
.onClick(() => {
this.num += 5;
})
}
.width('100%')
}
}
上記の例では、Index ページには 2 つのカスタムコンポーネントが含まれています。一つは @Entry で装飾された MyComponent で、ページのエントリコンポーネント、つまりページのルートノードです。もう一つは Child で、MyComponent の子コンポーネントです。@Entry で装飾されたノードのみがページレベルのライフサイクルメソッドを有効にすることができるため、MyComponent で現在の Index ページのページライフサイクル関数(onPageShow /onPageHide/onBackPress)を宣言しています。MyComponent とその子コンポーネント Child は、それぞれのコンポーネントレベルのライフサイクル関数(aboutToAppear /onDidBuild/aboutToDisappear)を宣言しています。
-
アプリケーションのコールドスタートの初期化プロセスは次のようになります:MyComponent aboutToAppear --> MyComponent build --> MyComponent onDidBuild --> Child aboutToAppear --> Child build --> Child onDidBuild --> Index onPageShow。
-
「delete Child」をクリックすると、if にバインドされた this.showChild が false になり、Child コンポーネントが削除され、Child aboutToDisappear メソッドが実行されます。
-
「push to next page」をクリックすると、router.pushUrl インターフェースが呼び出され、別のページに移動します。現在の Index ページが非表示になり、ページライフサイクル Index onPageHide が実行されます。この場合、router.pushUrl インターフェースが呼び出され、Index ページは非表示になり、破棄されていないため、onPageHide のみが呼び出されます。新しいページに移動すると、新しいページのライフサイクルの初期化プロセスが実行されます。
-
router.replaceUrl が呼び出された場合、現在の Index ページは破棄され、実行されるライフサイクルプロセスは次のようになります:Index onPageHide --> MyComponent aboutToDisappear --> Child aboutToDisappear。前述のように、コンポーネントの破棄はコンポーネントツリーから直接サブツリーを取り外すため、まず親コンポーネントの aboutToDisappear が呼び出され、その後子コンポーネントの aboutToDisappear が呼び出され、次に新しいページのライフサイクルの初期化プロセスが実行されます。
-
戻るボタンをクリックすると、ページライフサイクル Index onBackPress がトリガーされ、1 つのページに戻ると現在の Index ページが破棄されます。
-
アプリケーションを最小化するか、アプリケーションがバックグラウンドに入ると、Index onPageHide がトリガーされます。現在の Index ページは破棄されていないため、コンポーネントの aboutToDisappear は実行されません。アプリケーションがフォアグラウンドに戻ると、Index onPageShow が実行されます。
-
アプリケーションを終了すると、Index onPageHide --> MyComponent aboutToDisappear --> Child aboutToDisappear が実行されます。
カスタムコンポーネントがページライフサイクルを監視する#
無感でページルーティングを監視する能力を使用すると、カスタムコンポーネント内でページのライフサイクルを監視することができます。
// Index.ets
import { uiObserver, router, UIObserver } from '@kit.ArkUI';
@Entry
@Component
struct Index {
listener: (info: uiObserver.RouterPageInfo) => void = (info: uiObserver.RouterPageInfo) => {
let routerInfo: uiObserver.RouterPageInfo | undefined = this.queryRouterPageInfo();
if (info.pageId == routerInfo?.pageId) {
if (info.state == uiObserver.RouterPageState.ON_PAGE_SHOW) {
console.log(`Index onPageShow`);
} else if (info.state == uiObserver.RouterPageState.ON_PAGE_HIDE) {
console.log(`Index onPageHide`);
}
}
}
aboutToAppear(): void {
let uiObserver: UIObserver = this.getUIContext().getUIObserver();
uiObserver.on('routerPageUpdate', this.listener);
}
aboutToDisappear(): void {
let uiObserver: UIObserver = this.getUIContext().getUIObserver();
uiObserver.off('routerPageUpdate', this.listener);
}
build() {
Column() {
Text(`このページは ${this.queryRouterPageInfo()?.pageId} です`)
.fontSize(25)
Button("自分をプッシュ")
.onClick(() => {
router.pushUrl({
url: 'pages/Index'
})
})
Column() {
SubComponent()
}
}
}
}
@Component
struct SubComponent {
listener: (info: uiObserver.RouterPageInfo) => void = (info: uiObserver.RouterPageInfo) => {
let routerInfo: uiObserver.RouterPageInfo | undefined = this.queryRouterPageInfo();
if (info.pageId == routerInfo?.pageId) {
if (info.state == uiObserver.RouterPageState.ON_PAGE_SHOW) {
console.log(`SubComponent onPageShow`);
} else if (info.state == uiObserver.RouterPageState.ON_PAGE_HIDE) {
console.log(`SubComponent onPageHide`);
}
}
}
aboutToAppear(): void {
let uiObserver: UIObserver = this.getUIContext().getUIObserver();
uiObserver.on('routerPageUpdate', this.listener);
}
aboutToDisappear(): void {
let uiObserver: UIObserver = this.getUIContext().getUIObserver();
uiObserver.off('routerPageUpdate', this.listener);
}
build() {
Column() {
Text(`SubComponent`)
}
}
}
この記事は Mix Space によって xLog に同期更新されました。原始リンクは http://www.sroxck.top/posts/harmony/arkts-life