Vue での親子コンポーネント通信と、sync を使用して親子コンポーネント データを同期する

Vue での親子コンポーネント通信と、sync を使用して親子コンポーネント データを同期する

序文

親子コンポーネントの通信は、次の 2 つのケースに分けられます。

1. 親コンポーネントが子コンポーネントにデータを渡す
2. 子コンポーネントが親コンポーネントにデータを渡す

一般的に言えば、状況 1 のデータ転送の問題は props によって解決できるため、ここでは詳細には触れません。

子コンポーネントは親コンポーネントにデータを渡す

主に2つのシナリオの実現について話しますが、方法は3つあります。

1. 親コンポーネントは、props を通じて、データとデータを変更する関数を子コンポーネントに渡します。子コンポーネントで親コンポーネントから渡された関数を呼び出すことで、親コンポーネントのデータが更新されます (親コンポーネントにデータが渡されます) (子コンポーネントでは対応する応答イベントが必要です)

2. 子コンポーネントでカスタムイベント(vm.$emit)をトリガーすると、v-on: [カスタムイベント]を使用してデータが親コンポーネントに返され、関数をリッスンします。

3. ref を通じて子コンポーネントをマークすると、親コンポーネントは vm.$refs.[子コンポーネント ref].[子コンポーネントのプロパティ/メソッド] を通じて子コンポーネントのデータを直接取得できます。

以下に一つずつ紹介します

1. 親コンポーネントから子コンポーネントにpropsを介して関数を渡し、親コンポーネントのデータを変更する関数を呼び出す

ここにはコードは表示されません

まず、これは比較的単純であり、次に、これは明らかに Vue のベストプラクティスではありません (React ではより一般的です)。
コードを確認したい場合は、こちらをご覧ください: 「【Vue】Vue のさまざまなシナリオにおけるコンポーネント間のデータ交換に関する簡単な説明」 http://www.cnblogs.com/penghuwan/p/7286912.html (兄弟コンポーネント間のデータ交換のセクション)

2. カスタムイベントを通じて子コンポーネントから親コンポーネントにデータを渡す

$emit(event, [...parameters]) を通じて子コンポーネントでカスタムイベントをトリガーできるため、親コンポーネントは v-on を直接使用して、子コンポーネントが使用されている場所で子コンポーネントによってトリガーされたイベントをリッスンし、リスニング関数で子コンポーネントから渡されたすべてのパラメータを順番に取得できます。

例えば:
サブコンポーネントに記述します:

this.emit('eventYouDefined', arg);

次に、親コンポーネントの子コンポーネント テンプレートでリッスンできます。
// 親コンポーネントのテンプレートは次のとおりです:

<Son v-on: eventYouDefined = "functionYours" />

ここに例があります

親コンポーネント

<テンプレート>
  <div id="父">
    <div>
       私は親コンポーネントであり、以下を受け取りました:
      {{ text || 'まだデータがありません' }}
      <son v-on:sendData='getSonText'></son>
    </div>
  </div>
</テンプレート>
 
<スクリプト>
'./son.vue' から son をインポートします。
エクスポートデフォルト{
  データ: 関数 () {
    戻る {
      文章: ''
    }
  },
  コンポーネント:
    息子:息子
  },
  メソッド: {
    getSonText (テキスト) {
      this.text = テキスト
    }
  }
}

</スクリプト>
 
<スタイルスコープ>
#父 div {
  パディング: 10px;
  マージン: 10px;
  境界線: 1px のグレー実線;
  オーバーフロー: 非表示;
}
</スタイル>

サブコンポーネント:

<テンプレート>
  <div>
    <p>私は子コンポーネントです。私が持っているデータは次のとおりです: {{ text }}</p>
    <ボタン @click="sendData">
      データを送信</button>
  </div>
</テンプレート>
 
<スクリプト>
エクスポートデフォルト{
  データ () {
    戻る {
      テキスト: '子コンポーネントからのデータ'
    }
  },
  メソッド: {
    送信データ() {
      this.$emit('sendData', this.text)
    }
  }
}
</スクリプト>
 
<!-- CSS をこのコンポーネントのみに制限するために "scoped" 属性を追加します -->
<スタイルスコープ>
   ボタン { float: 左 }
</スタイル>

子コンポーネントの「データの送信」ボタンをクリックする前に、親コンポーネントがデータを受信して​​いない(テキストが空の文字列)場合、{{ text || 'データはまだありません' }} はデフォルトのテキスト「データはまだありません」を表示します。

「データを送信」ボタンをクリックした後:

sendDataカスタムイベントがトリガーされるため、

this.$emit('sendData', this.text) //ここでは子コンポーネントのインスタンスを指します)

子コンポーネントのテキスト データは親コンポーネントにあります。

 <son v-on:sendData='getSonText'></son>

コンポーネント内の getSonText 関数はパラメータをパラメータとして受け取り、子コンポーネントから親コンポーネントへのパラメータ転送プロセスを完了します。

3. ref属性を介して親コンポーネント内で子コンポーネントのデータを直接取得する

上で説明した処理シナリオ 1 と 2 には、両方ともイベント メカニズム (クリックなどのネイティブ イベントかカスタム イベントか) に基づく必要があり、関数はイベントが発生したときにのみ呼び出されてデータを渡すことができるという制限があります。

しかし、子コンポーネントに「ボタン」のようなものが存在せず、ネイティブ イベントを作成できず、カスタム イベントをトリガーするタイミングを見つける方法がない場合は、どのようにして子コンポーネントから親コンポーネントにデータを渡すことができるのでしょうか。 ?

現時点では、ref属性を使用して親コンポーネントから子コンポーネントのデータを「直接取得」することしかできません。

Ref はよく使用する Vue 属性です。これを使用すると、このコンポーネントのテンプレートから DOM インスタンスを簡単に便利に取得できます。実際、親コンポーネントで子コンポーネントの ref を設定すると、vm.$refs.[子コンポーネントの ref].[子コンポーネントの属性] を通じてデータを直接取得できます。例:

親コンポーネント:

<テンプレート>
  <div id="父">
    <div>
       私は親コンポーネントであり、以下を受け取りました:
      {{ text || 'まだデータがありません' }}
      <button @click="getSonText()">データを受け入れる</button>
      <息子ref='息子'></息子>
    </div>
  </div>
</テンプレート>
 
<スクリプト>
'./son.vue' から son をインポートします。
エクスポートデフォルト{
  データ: 関数 () {
    戻る {
      文章: ''
    }
  },
  コンポーネント:
    息子:息子
  },
  メソッド: {
    テキストを取得する() {
      this.text = this.$refs.son.text
    }
  }
}

</スクリプト>
 
<スタイルスコープ>
#父 div {
  パディング: 10px;
  マージン: 10px;
  境界線: 1px の灰色
  オーバーフロー: 非表示;
}
</スタイル>

サブコンポーネント:

<テンプレート>
  <div>
    <p>私は子コンポーネントです。私が持っているデータ: {{ text }}</p>
  </div>
</テンプレート>
 
<スクリプト>
エクスポートデフォルト{
  データ () {
    戻る {
      テキスト: '子コンポーネントからのデータ'
    }
  }
}
</スクリプト>
 
<!-- CSS をこのコンポーネントのみに制限するために "scoped" 属性を追加します -->
<スタイルスコープ>
   ボタン { float: 左 }
</スタイル>

デモ:

「データを受け入れる」ボタンをクリックする前に:

「データを受け入れる」ボタンをクリックした後:

同期を使用して双方向のデータバインディングを実現し、親子コンポーネントのデータを同期します。

上記の 3 つの方法により、親子コンポーネントの通信シナリオのほとんどを解決できると思いますが、上記の通信シナリオを注意深く考えてみると、まだ問題があることがわかります。

子コンポーネントから親コンポーネントにデータを渡す場合、親コンポーネントと子コンポーネントのデータは常に同期されるわけではありません。

しかし、特別な要求のシナリオでは、親コンポーネントと子コンポーネントのデータを常に同期させたい場合があります。この場合、次のようにします。

これは親コンポーネントのテンプレートです:

<son :foo="bar" v-on:update="val => bar = val"></son>

子コンポーネントでは、props宣言を通じてfooを受け取り、

小道具: {
     foo: [タイプ]
}

同時に、サブコンポーネント内のデータが変更されるたびに、

this.$emit('update', newValue)

親コンポーネント テンプレートのリスニング関数の「val」にパラメーター newValue を渡します。そしてパス

val => バー = val

この式は、bar = newValue を実装します。この時点で、親コンポーネントのキー データ bar が子コンポーネントによって変更 (等しい) されていることがわかります。

双方向データバインディングにより、親(コンポーネント)は子のデータを変更でき、子も親のデータを変更できる。

Vue は上記のコードを簡略化するために sync 修飾子を提供します。次に例を示します。

<comp :foo.sync="bar"></comp>

次のように拡張されます:

<comp :foo="bar" @update:foo="val => bar = val"></comp>

次に、子コンポーネント内の親コンポーネントのデータを変更する必要がある場合は、次のカスタム イベントをトリガーする必要があります。

this.$emit("update:foo", newValue)

[注意] これは、前述の「カスタム イベント (emit) を介して子コンポーネントから親コンポーネントにデータを渡す」セクションの内容と重複しているように感じられるかもしれません。

しかし、そうではありません。両者の親子コンポーネント関係には違いがあります。以下では、重要なコード行を使用してその違いを証明します。

1. 同期について説明するセクションでは、カスタム イベントが発生したときに実行される応答式は次のとおりです。
<son :foo="bar" v-on:update="val => bar = val"></son> 内の "val => bar = val"

2. 「カスタム イベントを通じて子コンポーネントから親コンポーネントにデータを渡す」の 2 番目の部分では、カスタム イベントが発生したときに実行される応答式は次のとおりです。
<Son v-on: eventYouDefined = "arg => functionYours(arg)" />

前者の場合、式 val => bar = val は、親コンポーネントのデータが子コンポーネントから渡されたデータと等しくなるように強制することを意味します。 このとき、親コンポーネントと子コンポーネントの状態が等しいことがわかります。 親は子(データ)を変更でき、子も親(データ)を変更できる

後者の場合、関数 Yours は親コンポーネントで定義されます。この関数では、子コンポーネントから受け取った arg データに対して任意の操作や処理を実行できます。決定権は完全に親コンポーネントにあります。つまり、親は子 (データ) を変更できますが、子は親 (データ) を直接変更することはできません。親のデータの変更は、それ自体によってのみ判断できる。

デモはこちらです:

親コンポーネント:

<テンプレート>
  <div id="父">
    <div>
       私は親コンポーネントです<息子
        :wisdom.sync="知恵"
        :magic.sync="マジック"
        :attack.sync="攻撃"
        :defense.sync="防御">
      </息子>
      <p>知性: {{ 知恵 }}</p>
      <p>膜法: {{ magic }}</p>
      <p>攻撃: {{ attack }}</p>
      <p>防御: {{defense }}}</p>
    </div>
  </div>
</テンプレート>
 
<スクリプト>
'./son.vue' から son をインポートします。
エクスポートデフォルト{
  データ: 関数 () {
    戻る {
      知恵: 90,
      魔法: 160,
      攻撃力: 100,
      防御力: 80
    }
  },
  コンポーネント:
    息子:息子
  }
}

</スクリプト>
 
<スタイルスコープ>
#父 div {
  パディング: 10px;
  マージン: 10px;
  境界線: 1px の灰色
  オーバーフロー: 非表示;
}
</スタイル>

サブコンポーネント:

<テンプレート>
  <div>
    <p>私はサブコンポーネントです</p>
    <p>知性: {{ 知恵 }}</p>
    <p>膜法: {{ magic }}</p>
    <p>攻撃: {{ attack }}</p>
    <p>防御: {{defense }}}</p>
    <button @click="increment('wisdom')">知性を高める</button>
    <button @click="increment('magic')">膜を増やす方法</button>
    <button @click="increment('attack')">攻撃力を上げる</button>
    <button @click="increment('defense')">防御力を上げる</button>
  </div>
</テンプレート>
 
<スクリプト>
エクスポートデフォルト{
  小道具: {
    知恵:数、
    魔法: 数字、
    攻撃: 数、
    防御: 番号
  },

  メソッド: {
    増分(データ名) {
      newValue = this[dataName] + 1 とします
      this.$emit(`update:${dataName}`, newValue)
    }
  }
}
</スクリプト>
 
<!-- CSS をこのコンポーネントのみに制限するために "scoped" 属性を追加します -->
<スタイルスコープ>
   ボタン { float: 左 }
</スタイル>

クリック前

サブコンポーネントの追加で「インテリジェンスの増加」ボタンをクリックすると、親コンポーネントとサブコンポーネントの両方のインテリジェンス パラメータが 90 から 91 に変更されます。

「サブコンポーネントの追加」で「膜メソッドの追加」ボタンをクリックすると、親コンポーネントとサブコンポーネントのインテリジェンス パラメータが同時に 160 から 161 に変更されます。

双方向データバインディングは諸刃の剣である

利点から:

1. 親子コンポーネントデータの「リアルタイム」同期を実現し、いくつかのデータシナリオで使用できます。
2. syncが提供する構文糖により、双方向バインディングのコードが非常にシンプルになります。

欠点としては:

これにより、一方向のデータフローのシンプルさが損なわれ、データの分析の難易度が高まります。

同期によって変更されたプロパティがオブジェクトの場合

上記の例を修正し、データをオブジェクトにラップして渡してみましょう。

親コンポーネント

<テンプレート>
  <div id="父">
    <div>
       私は親コンポーネントです <son :analysisData.sync="analysisData">
      </息子>
      <p>インテリジェンス: {{ analysisData.wisdom }}</p>
      <p>膜法: {{ analysisData.magic }}</p>
      <p>攻撃: {{ analysisData.attack }}</p>
      <p>防御: {{ analysisData.defense }}</p>
    </div>
  </div>
</テンプレート>
 
<スクリプト>
'./son.vue' から son をインポートします。
エクスポートデフォルト{
  データ: 関数 () {
    戻る {
      分析データ:
        知恵: 90,
        魔法: 160,
        攻撃力: 100,
        防御力: 80
      }
    }
  },
  コンポーネント:
    息子:息子
  }
}

</スクリプト>
 
<スタイルスコープ>
#父 div {
  パディング: 10px;
  マージン: 10px;
  境界線: 1px の灰色
  オーバーフロー: 非表示;
}
</スタイル>

サブコンポーネント

<テンプレート>
  <div>
    <p>私はサブコンポーネントです</p>
    <p>インテリジェンス: {{ analysisData.wisdom }}</p>
    <p>膜法: {{ analysisData.magic }}</p>
    <p>攻撃: {{ analysisData.attack }}</p>
    <p>防御: {{ analysisData.defense }}</p>
    <button @click="increment('wisdom')">知性を高める</button>
    <button @click="increment('magic')">膜を増やす方法</button>
    <button @click="increment('attack')">攻撃力を上げる</button>
    <button @click="increment('defense')">防御力を上げる</button>
  </div>
</テンプレート>
 
<スクリプト>
エクスポートデフォルト{
  小道具: {
    分析データ: オブジェクト
  },

  メソッド: {
    増分(データ名) {
      newObj = JSON.parse(JSON.stringify(this.analysisData)) とします。
      新しいオブジェクト[データ名] += 1
      this.$emit('update:analysisData', newObj)
    }
  }
}
</スクリプト>
 
<!-- CSS をこのコンポーネントのみに制限するために "scoped" 属性を追加します -->
<スタイルスコープ>
   ボタン { float: 左 }
</スタイル>

上記と同じデモ

子コンポーネントの参照型プロパティを変更することで、「親子コンポーネントのデータ同期」の要件を満たさないでください。

親コンポーネントのデータは子コンポーネントに渡されますが、これは通常、props を通じて実現されます。「親子コンポーネントのデータ同期」の要件を実装する場合、子コンポーネント内の参照型 (配列やオブジェクトなど) の props を変更することが実現可能であることがわかります。

1. 親コンポーネントのデータを同時に変更できるだけでなく(同じデータが元々参照されているため)
2. そして、Vue の検出メカニズムでは検出されません。 (エラーは報告されません)

しかし、これを行わないでください。データフローの分析が非常に難しくなります。これを試す場合は、上記のアプローチの方がおそらく優れています。これを行わないでください。悪いアプローチです。

親コンポーネント:

<テンプレート>
  <div id="父">
    <div>
       私は親コンポーネントです <son :analysisData="analysisData">
      </息子>
      <p>インテリジェンス: {{ analysisData.wisdom }}</p>
      <p>膜法: {{ analysisData.magic }}</p>
      <p>攻撃: {{ analysisData.attack }}</p>
      <p>防御: {{ analysisData.defense }}</p>
    </div>
  </div>
</テンプレート>
 
<スクリプト>
'./son.vue' から son をインポートします。
エクスポートデフォルト{
  データ: 関数 () {
    戻る {
      分析データ:
        知恵: 90,
        魔法: 160,
        攻撃力: 100,
        防御力: 80
      }
    }
  },
  コンポーネント:
    息子:息子
  }
}

</スクリプト>
 
<スタイルスコープ>
#父 div {
  パディング: 10px;
  マージン: 10px;
  境界線: 1px の灰色
  オーバーフロー: 非表示;
}
</スタイル>

サブコンポーネント:

<テンプレート>
  <div>
    <p>私はサブコンポーネントです</p>
    <p>インテリジェンス: {{ analysisData.wisdom }}</p>
    <p>膜法: {{ analysisData.magic }}</p>
    <p>攻撃: {{ analysisData.attack }}</p>
    <p>防御: {{ analysisData.defense }}</p>
    <button @click="increment ('wisdom')">知性を高める</button>
    <button @click="increment ('magic')">膜を増やす方法</button>
    <button @click="increment ('attack')">攻撃力を上げる</button>
    <button @click="increment ('defense')">防御力を上げる</button>
  </div>
</テンプレート>
 
<スクリプト>
エクスポートデフォルト{
  小道具: {
    分析データ: オブジェクト
  },

  メソッド: {
    増分(データ名) {
      obj = this.analysisData とします。
      obj[データ名] += 1
    }
  }
}
</スクリプト>
 
<!-- CSS をこのコンポーネントのみに制限するために "scoped" 属性を追加します -->
<スタイルスコープ>
   ボタン { float: 左 }
</スタイル>

以上が、Vue における親子コンポーネント通信の詳細と、sync を使用して親子コンポーネントのデータを同期する方法です。Vue の詳細については、123WORDPRESS.COM の他の関連記事にも注目してください。

以下もご興味があるかもしれません:
  • Vue2とVue3の兄弟コンポーネント通信バスの違いと使い方
  • Vue.js 親子コンポーネント通信開発例
  • Vue での親子コンポーネント通信における todolist コンポーネント機能の開発
  • Vue2 における 12 種類のコンポーネント通信
  • Vue3 の 10 個のコンポーネント通信方法の概要

<<:  Mysql データベース ストアド プロシージャの基本構文の説明

>>:  Linux でタイムアウト付きの接続関数を試す

推薦する

Nofollowはコメントやメッセージ内のリンクを本当に機能させる

コメントとメッセージはもともと、ウェブマスターがコミュニティと読者層を構築するための優れた手段でした...

IE6 フォントを定義できません: 13px サイズは無効です。IE6 は自動的に大きいフォント ソリューションを表示します。

数日前、Web ページのモジュールを調整していたとき、ページのフォント サイズを 13px に設定し...

vue-cli の紹介とインストール

目次1. はじめに2. vue-cli の紹介2.1 コマンドライン2.2 CLI サービス2.3 ...

MySQL 起動失敗の問題とシナリオ分析

1. ワンストップソリューション1. 問題の分析と特定 # MySQL設定ファイルを見つけて、MyS...

広告を閉じる効果を実現するJavascript

参考までに、Javascript を使用して広告を閉じる方法に関するケース スタディを示します。詳細...

Ubuntu 14.04 で QT5 をインストール、設定、アンインストールするための詳細な手順

1. 以前 QT5.13 バージョンをダウンロードしましたが、インストール後、Qtcreator を...

Vue3+Vantコンポーネントを使用してアプリの検索履歴機能を実装する(サンプルコード)

現在、新しいアプリプロジェクトを開発中です。私にとっても初めてのアプリ開発です。チームで調査と検討を...

アイデアがWebプロジェクトを公開した後、Tomcatサーバーがプロジェクトとそのソリューションを見つけることができません

概要プロジェクトは正常に作成され、正常にデプロイされましたが、以下に示すように、Tomcat サーバ...

フレックスレイアウトが子要素によって引き伸ばされたときに、コンテンツをコンテナ内に保持する方法

モバイル デバイスでは、フレックス レイアウトが非常に便利です。デバイスの幅に応じてコンテナーの幅を...

Vue3 でマークダウン エディター コンポーネントを使用する方法

目次インストールコンポーネントのインポート基本的な使い方保存したマークダウンまたは HTML テキス...

HTMLフォーム送信方法のケーススタディ

フォームの送信方法をまとめると次のようになります。 1. 送信ボタンを使用して送信します。送信ボタン...

Vue プロジェクトで axios をカプセル化する方法 (http リクエストの統合管理)

1. 要件Vue.js フレームワークを使用してフロントエンド プロジェクトを開発する場合、サーバ...

CSS グラデーション効果の概要 (線形グラデーションと放射状グラデーション)

線形グラデーション 背景画像: linear-gradient(方向、開始色、中間色1、中間色2、....

位置のいくつかの巧妙な応用の詳細な説明:sticky スティッキーポジショニング

背景: position:sticky はスティッキー配置とも呼ばれます。スティッキー配置の要素は、...

MySQL 8.0.15 winx64 のインストールと設定方法のグラフィックチュートリアル (Windows の場合)

この記事では、MySQL 8.0.15 winx64のインストールと設定方法を参考までに紹介します。...