@Link デコレータ: 親子双方向同期
子コンポーネント内の @Link で修飾された変数は、親コンポーネント内の対応するデータ ソースとの双方向データ バインディングを確立します。
概要
@Link で修飾された変数は、親コンポーネント内のデータ ソースと同じ値を共有します。
デコレータの使用ルールの説明
@Link変数デコレータ |
説明する |
---|---|
デコレータパラメータ |
なし |
同期タイプ |
双方向同期。 親コンポーネントの @State、@StorageLink、@Link と子コンポーネントの @Link は双方向のデータ同期を確立でき、その逆も可能です。 |
装飾が可能な変数型 |
オブジェクト、クラス、文字列、数値、ブール型、列挙型、およびこれらの型の配列。 型は指定する必要があり、双方向バインディング状態変数の型と同じです。 Any はサポートされず、単純型と複合型の共用体型はサポートされず、未定義と null は許可されません。 説明する Length、ResourceStr、および ResourceColor 型はサポートされていません。Length、ResourceStr、および ResourceColor は、単純型と複合型の共用体型です。 |
修飾された変数の初期値 |
なし、ローカル初期化を無効にします。 |
変数の転送・アクセスルールの説明
パス/アクセス |
説明する |
---|---|
親コンポーネントからの初期化と更新 |
必須。親コンポーネント @State、@StorageLink、および @Link を使用して双方向バインディングを作成します。親コンポーネントの @State、@Link、@Prop、@Provide、@Consume、@ObjectLink、@StorageLink、@StorageProp、@LocalStorageLink、@LocalStorageProp 装飾変数を使用して子コンポーネント @Link を初期化できるようにします。 API バージョン 9 以降、@Link 子コンポーネントは構文 Comp({ aLink: this.aState }) を使用して親コンポーネントから @State を初期化します。Comp({aLink: $aState}) もサポートされています。 |
子コンポーネントの初期化に使用されます |
許可され、通常の変数、@State、@Link、@Prop、@Provide の初期化に使用できます。 |
コンポーネント外部へのアクセスをサポートするかどうか |
プライベートであり、所有コンポーネント内でのみアクセスできます。 |
図 1 初期化ルールの図
変化や行動を観察する
変化を観察する
- 装飾のデータ型がブール、文字列、または数値の場合、値の変化を同期的に監視できます。
- 装飾されたデータ型がクラスまたはオブジェクトの場合、割り当てと属性の割り当ての変更、つまり Object.keys(observedObject) によって返されるすべての属性を観察できます。
- 装飾されたオブジェクトが配列の場合、配列内の配列セルの追加、削除、更新の変化を観察できます。
フレームの動作
@Link で装飾された変数は、それらが記述するカスタム コンポーネントとライフサイクルを共有します。
@Link 変数の初期化と更新のメカニズムを理解するには、親コンポーネントと @Link 変数を所有する子コンポーネントの間の関係、初期レンダリングと双方向更新のプロセス (親コンポーネントを @ として受け取ります) を理解する必要があります。例として記載します)。
- 初期レンダリング: 親コンポーネントの build() 関数の実行後に、子コンポーネントの新しいインスタンスが作成されます。初期化プロセスは次のとおりです。
- 子コンポーネントの @Link 変数を初期化するには、親コンポーネントの @State 変数を指定する必要があります。子コンポーネントの @Link 変数値は、親コンポーネントのデータ ソース変数と同期されます (双方向データ同期)。
- 親コンポーネントの @State 状態変数ラッパー クラスは、コンストラクターを通じて子コンポーネントに渡されます。子コンポーネントの @Link ラッパー クラスは、親コンポーネントの @State 状態変数を取得した後、現在の @Link ラッパー クラスを登録します親コンポーネントの @Link ラッパー クラスへのポインタ 状態変数。
- @Link のデータ ソースの更新、つまり、親コンポーネントの状態変数の更新により、関連する子コンポーネントの @Link も更新されます。処理手順:
- 初期レンダリングの手順を通じて、子コンポーネントの @Link ラッパー クラスが現在の this ポインターを親コンポーネントに登録していることがわかります。親コンポーネントの @State 変数が変更されると、それに依存するすべてのシステム コンポーネント (elementid) と状態変数 (@Link ラッパー クラスなど) が走査されて更新されます。
- @Link ラッパー クラスの更新を通知すると、サブコンポーネント内の @Link 状態変数に依存するすべてのシステム コンポーネント (elementId) に更新が通知されます。このようにして、親コンポーネントから子コンポーネントへの状態データの同期が実現されます。
- @Link の更新: 子コンポーネントの @Link が更新される場合、処理手順は次のとおりです (例として親コンポーネントを @State とします)。
- @Link が更新された後、親コンポーネントの @State ラッパー クラスの set メソッドを呼び出して、更新された値を親コンポーネントに同期させます。
- 子コンポーネント @Link と親コンポーネント @State はそれぞれ、依存するシステム コンポーネントを横断し、対応する UI を更新します。このようにして、子コンポーネント @Link が親コンポーネント @State に同期されます。
使用するシーン
単純型とオブジェクトのような型の @Link
次の例では、親コンポーネント ShufflingContainer の「親ビュー: Set yellowButton」および「親ビュー: Set GreenButton」をクリックして、親コンポーネントから子コンポーネントへの変更と、子コンポーネントの @Link 装飾変数の変更を同期します。 GreenButton および YellowButton コンポーネントも、その親コンポーネントに同期されます。
class GreenButtonState {
width: number = 0;
constructor(width: number) {
this.width = width;
}
}
@Component
struct GreenButton {
@Link greenButtonState: GreenButtonState;
build() {
Button('Green Button')
.width(this.greenButtonState.width)
.height(150.0)
.backgroundColor('#00ff00')
.onClick(() => {
if (this.greenButtonState.width < 700) {
// 更新class的属性,变化可以被观察到同步回父组件
this.greenButtonState.width += 125;
} else {
// 更新class,变化可以被观察到同步回父组件
this.greenButtonState = new GreenButtonState(100);
}
})
}
}
@Component
struct YellowButton {
@Link yellowButtonState: number;
build() {
Button('Yellow Button')
.width(this.yellowButtonState)
.height(150.0)
.backgroundColor('#ffff00')
.onClick(() => {
// 子组件的简单类型可以同步回父组件
this.yellowButtonState += 50.0;
})
}
}
@Entry
@Component
struct ShufflingContainer {
@State greenButtonState: GreenButtonState = new GreenButtonState(300);
@State yellowButtonProp: number = 100;
build() {
Column() {
// 简单类型从父组件@State向子组件@Link数据同步
Button('Parent View: Set yellowButton')
.onClick(() => {
this.yellowButtonProp = (this.yellowButtonProp < 700) ? this.yellowButtonProp + 100 : 100;
})
// class类型从父组件@State向子组件@Link数据同步
Button('Parent View: Set GreenButton')
.onClick(() => {
this.greenButtonState.width = (this.greenButtonState.width < 700) ? this.greenButtonState.width + 100 : 100;
})
// class类型初始化@Link
GreenButton({ greenButtonState: $greenButtonState })
// 简单类型初始化@Link
YellowButton({ yellowButtonState: $yellowButtonProp })
}
}
}
配列型の@Link
@Component
struct Child {
@Link items: number[];
build() {
Column() {
Button(`Button1: push`).onClick(() => {
this.items.push(this.items.length + 1);
})
Button(`Button2: replace whole item`).onClick(() => {
this.items = [100, 200, 300];
})
}
}
}
@Entry
@Component
struct Parent {
@State arr: number[] = [1, 2, 3];
build() {
Column() {
Child({ items: $arr })
ForEach(this.arr,
item => {
Text(`${item}`)
},
item => item.toString()
)
}
}
}
上で述べたように、ArkUI フレームワークは配列要素の追加、削除、置換を観察できます。この例では、@State と @Link の型が同じnumber[]であるため、@Linkを数値型(@Link項目:number)として定義し、@Stateの各データ項目で作成することはできません。親コンポーネントのサブアセンブリ内の配列。
@Provide デコレータと @Consume デコレータ: 子孫コンポーネントとの双方向同期
@Provide と @Consume は、子孫コンポーネントとの双方向のデータ同期に適用され、状態データが複数のレベル間で受け渡されるシナリオに適用されます。前述の名前付きパラメータ機構による親コンポーネントと子コンポーネント間の受け渡しとは異なり、@Provide と @Consume はパラメータ受け渡し機構の束縛を取り除き、レベル間の受け渡しを実現します。
@Provide で修飾された変数は祖先ノードにあり、子孫に「提供」される状態変数として理解できます。@Consume で修飾された変数は、祖先ノードによって提供された変数を子孫コンポーネントで「消費 (バインド)」します。
概要
@Provide/@Consume で修飾された状態変数には次の特性があります。
- @Provide によって修飾された状態変数は、そのすべての子孫コンポーネントで自動的に利用可能になります。つまり、変数はその子孫コンポーネントに「提供」されます。@Provide の利便性は、開発者がコンポーネント間で変数を何度も渡す必要がないことであることがわかります。
- 子孫は @Consume を使用して @Provide によって提供される変数を取得し、@Provide と @Consume の間で双方向のデータ同期を確立します。@State/@Link とは異なり、前者はマルチレベルの親子コンポーネント間で受け渡すことができます。
- @Provide と @Consume は、同じ変数名または同じ変数エイリアスでバインドできます。また、変数の型は同じである必要があります。
// 通过相同的变量名绑定
@Provide a: number = 0;
@Consume a: number;
// 通过相同的变量别名绑定
@Provide('a') b: number = 0;
@Consume('a') c: number;
デコレータの説明
@State のルールは @Provide にも適用されますが、異なる点は、@Provide が複数の子孫の同期ソースとしても機能することです。
@変数デコレータを提供する |
説明する |
---|---|
デコレータパラメータ |
エイリアス: const 文字列、オプション。 別名が指定されている場合、変数はその別名によってバインドされ、別名が指定されていない場合、変数は変数名によってバインドされます。 |
同期タイプ |
双方向同期。 @Provide 変数からすべての @Consume 変数へ、またはその逆のデータ同期。双方向同期の動作は@Stateと@Linkの組み合わせと同じです。 |
装飾が可能な変数型 |
オブジェクト、クラス、文字列、数値、ブール型、列挙型、およびこれらの型の配列。 Any はサポートされず、単純型と複合型の共用体型はサポートされず、未定義と null は許可されません。 タイプを指定する必要があります。@Consume 変数は @Provide 変数と同じ型である必要があります。 説明する Length、ResourceStr、および ResourceColor 型はサポートされていません。Length、ResourceStr、および ResourceColor は、単純型と複合型の共用体型です。 |
修飾された変数の初期値 |
を指定する必要があります。 |
@Consume変数デコレータ |
説明する |
---|---|
デコレータパラメータ |
エイリアス: const 文字列、オプション。 エイリアスが指定されている場合、正常に一致するには @Provide 変数に同じエイリアスが必要です。それ以外の場合、正常に一致するには変数名が同じである必要があります。 |
同期タイプ |
双方向: @Provide 変数 (詳細については @Provide を参照) からすべての @Consume 変数へ、またはその逆。双方向同期は、@State と @Link の組み合わせと同じように動作します。 |
装飾が可能な変数型 |
オブジェクト、クラス、文字列、数値、ブール型、列挙型、およびこれらの型の配列。 any はサポートされておらず、未定義および null も許可されません。 タイプを指定する必要があります。@Consume 変数は @Provide 変数と同じ型である必要があります。 説明する
|
修飾された変数の初期値 |
なし、ローカル初期化を無効にします。 |
変数の転送・アクセスルールの説明
@パス/アクセスを提供する |
説明する |
---|---|
親コンポーネントからの初期化と更新 |
可选,允许父组件中常规变量、@State、@Link、@Prop、@Provide、@Consume、@ObjectLink、@StorageLink、@StorageProp、@LocalStorageLink和@LocalStorageProp装饰的变量装饰变量初始化子组件@Provide。 |
用于初始化子组件 |
允许,可用于初始化@State、@Link、@Prop、@Provide。 |
和父组件同步 |
否。 |
和后代组件同步 |
和@Consume双向同步。 |
是否支持组件外访问 |
私有,仅可以在所属组件内访问。 |
图1 @Provide初始化规则图示
@Consume传递/访问 |
说明 |
---|---|
从父组件初始化和更新 |
禁止。通过相同的变量名和alias(别名)从@Provide初始化。 |
用于初始化子组件 |
允许,可用于初始化@State、@Link、@Prop、@Provide。 |
和祖先组件同步 |
和@Provide双向同步。 |
是否支持组件外访问 |
私有,仅可以在所属组件内访问 |
图2 @Consume初始化规则图示
观察变化和行为表现
观察变化
- 当装饰的数据类型为boolean、string、number类型时,可以观察到数值的变化。
- 当装饰的数据类型为class或者Object的时候,可以观察到赋值和属性赋值的变化(属性为Object.keys(observedObject)返回的所有属性)。
- 当装饰的对象是array的时候,可以观察到数组的添加、删除、更新数组单元。
框架行为
- 初始渲染:
- @Provide装饰的变量会以map的形式,传递给当前@Provide所属组件的所有子组件;
- 子组件中如果使用@Consume变量,则会在map中查找是否有该变量名/alias(别名)对应的@Provide的变量,如果查找不到,框架会抛出JS ERROR;
- 在初始化@Consume变量时,和@State/@Link的流程类似,@Consume变量会保存在map中查找到的@Provide变量,并把自己注册给@Provide。
- 当@Provide装饰的数据变化时:
- 通过初始渲染的步骤可知,子组件@Consume已把自己注册给父组件。父组件@Provide变量变更后,会遍历更新所有依赖它的系统组件(elementid)和状态变量(@Consume);
- 通知@Consume更新后,子组件所有依赖@Consume的系统组件(elementId)都会被通知更新。以此实现@Provide对@Consume状态数据同步。
- 当@Consume装饰的数据变化时:
- 通过初始渲染的步骤可知,子组件@Consume持有@Provide的实例。在@Consume更新后调用@Provide的更新方法,将更新的数值同步回@Provide,以此实现@Consume向@Provide的同步更新。
使用场景
在下面的示例是与后代组件双向同步状态@Provide和@Consume场景。当分别点击CompA和CompD组件内Button时,reviewVotes 的更改会双向同步在CompA和CompD中。
@Component
struct CompD {
// @Consume装饰的变量通过相同的属性名绑定其祖先组件CompA内的@Provide装饰的变量
@Consume reviewVotes: number;
build() {
Column() {
Text(`reviewVotes(${this.reviewVotes})`)
Button(`reviewVotes(${this.reviewVotes}), give +1`)
.onClick(() => this.reviewVotes += 1)
}
.width('50%')
}
}
@Component
struct CompC {
build() {
Row({ space: 5 }) {
CompD()
CompD()
}
}
}
@Component
struct CompB {
build() {
CompC()
}
}
@Entry
@Component
struct CompA {
// @Provide装饰的变量reviewVotes由入口组件CompA提供其后代组件
@Provide reviewVotes: number = 0;
build() {
Column() {
Button(`reviewVotes(${this.reviewVotes}), give +1`)
.onClick(() => this.reviewVotes += 1)
CompB()
}
}
}