Vuejs 2.0では、model.sync
は 非推奨 になります。
では、 vuejs 2. の兄弟コンポーネント間で通信する適切な方法は何ですか?
アイデア Vueで2が兄弟通信を行うことですストアまたはイベントバスを使用して。
evan によると:
また、「コンポーネント間でデータを渡す」ことは一般的に悪い考えです。最終的にはデータフローが追跡できなくなり、デバッグが非常に難しくなるためです。
データの一部を複数のコンポーネントで共有する必要がある場合は、 グローバルストア または Vuex を選択してください。
[ ディスカッションへのリンク ]
そして:
.once
および.sync
は非推奨です。小道具は常に一方通行です。親スコープで副作用を生成するには、コンポーネントは暗黙的なバインディングに依存するのではなく、明示的にイベントをemit
する必要があります。
(つまり、彼 提案 は$emit
と$on
を使用することです)
私が心配しているのは:
store
とevent
にはそれぞれグローバルな可視性があります(間違っている場合は修正してください)。私が欲しいのはscopeどういうわけかevents
またはstores
兄弟コンポーネントの可視性です。または、おそらく私はその考えを理解しませんでした。
だから、どのように正しい方法で通信しますか?
Vue 2.0では、 documentation で示されているようにeventHubメカニズムを使用しています。
集中型イベントハブを定義します。
const eventHub = new Vue() // Single event hub
// Distribute to components using global mixin
Vue.mixin({
data: function () {
return {
eventHub: eventHub
}
}
})
コンポーネントでイベントを発行できます
this.eventHub.$emit('update', data)
そして聞くために
this.eventHub.$on('update', data => {
// do your thing
})
Updateより単純なソリューションを説明する @ alex のanwerを参照してください。
さらに短くして、 rootVue
インスタンスをグローバルイベントハブとして使用することもできます。
コンポーネント1:
this.$root.$emit('eventing', data);
コンポーネント2:
mounted() {
this.$root.$on('eventing', data => {
console.log(data);
});
}
これは古い質問ですが、他のコミュニケーションチャネルを公開し、アプリとコミュニケーションをより高い視点から表示する方法を知りたかったのです。
Vueアプリケーション(または実際、コンポーネントベースのアプリケーション)を設計する際に最初に理解することは、処理する懸念に依存するさまざまな通信タイプがあり、独自の通信チャネルが必要であることです。 。
ビジネスロジック:は、アプリとその目標に固有のすべてを指します。
プレゼンテーションロジック:ユーザーがやり取りするもの、またはユーザーからのやり取りの結果であるもの。
これらの2つの懸念は、これらのタイプの通信に関連しています。
各タイプは適切な通信チャネルを使用する必要があります。
チャンネルは、Vueアプリの周りでデータを交換するための具体的な実装を指すために使用するおおまかな用語です。
直接親子通信用のVueの最も単純な通信チャネル。ほとんどの場合、プレゼンテーションロジックに関連するデータまたは制限されたデータセットを階層に渡すために使用する必要があります。
子を親からのイベントを処理するために小道具を使用する意味がない場合、 子コンポーネントでref
を設定し、そのメソッドを呼び出す で十分です。
一部の人々は、これは親と子の間の密結合であると言うかもしれませんが、小道具を使用するのと同じ結合です。小道具の契約に同意できれば、メソッドの契約にも同意できます。
$emit
および$on
。直接の親子通信のための最も簡単な通信チャネル。ここでも、プレゼンテーションロジックに使用する必要があります。
ほとんどの回答は、遠く離れたコンポーネント、または実際には何でも利用できる通信チャネルの1つであるイベントバスの優れた代替手段を提供します。
これは、他のコンポーネントがそれらを必要としない他のコンポーネントがほとんどない状態で、プロップを遠くから深くネストされた子コンポーネントに至る所で渡す場合に役立ちます。
注意:イベントバスに自身をバインドしているコンポーネントの後続の作成は、複数回バインドされます。複数のハンドラーがトリガーされ、リークが発生します。個人的に、過去にデザインしたすべてのシングルページアプリでイベントバスの必要性を感じたことはありませんでした。
次の例は、DOMから削除された場合でもItem
コンポーネントがトリガーされる単純なミスがどのようにリークにつながるかを示しています。
// A component that binds to a custom 'update' event.
var Item = {
template: `<li>{{text}}</li>`,
props: {
text: Number
},
mounted() {
this.$root.$on('update', () => {
console.log(this.text, 'is still alive');
});
},
};
// Component that emits events
var List = new Vue({
el: '#app',
components: {
Item
},
data: {
items: [1, 2, 3, 4]
},
updated() {
this.$root.$emit('update');
},
methods: {
onRemove() {
console.log('slice');
this.items = this.items.slice(0, -1);
}
}
});
<script src="https://unpkg.com/[email protected]/dist/vue.min.js"></script>
<div id="app">
<button type="button" @click="onRemove">Remove</button>
<ul>
<item v-for="item in items" :key="item" :text="item"></item>
</ul>
</div>
destroyed
ライフサイクルフックのリスナーを削除することを忘れないでください。
Vuex は、state managementのVueを使用する方法です。単なるイベント以上のものを提供し、本格的なアプリケーションに対応しています。
そして今 あなたが尋ねる :
[S]マイナーなコミュニケーションごとにvuexのストアを作成できますか?
本当に輝いているのは:
そのため、コンポーネントは本来の意味に焦点を当て、ユーザーインターフェイスを管理できます。
コンポーネントロジックに使用できないという意味ではありませんが、必要なグローバルUI状態のみを持つ名前空間Vuexモジュールにそのロジックをスコープします。
グローバルな状態にあるすべての大きな混乱に対処するために、複数の名前空間モジュールにストアを分割する必要があります。
これらすべての通信を調整し、再利用性を容易にするために、コンポーネントを2つの異なるタイプと考える必要があります。
繰り返しになりますが、汎用コンポーネントを再利用する必要があることや、アプリ固有のコンテナを再利用できないことを意味するわけではありませんが、責任は異なります。
これらは、他のVueコンポーネント(汎用または他のアプリ固有のコンテナー)をラップする単純なVueコンポーネントです。これは、Vuexストアの通信が発生する場所であり、このコンテナは、小道具やイベントリスナーなどの他の単純な手段を介して通信する必要があります。
これらのコンテナにはネイティブDOM要素がまったくなくてもよく、汎用コンポーネントでこれを処理できます。
scope何らかの理由で
events
またはstores
兄弟コンポーネントの可視性
これがスコーピングの発生場所です。ほとんどのコンポーネントはストアについて知らないため、このコンポーネントは(ほとんど)提供されたVuexマッパーで適用されるgetters
およびactions
の限定されたセットを持つ1つの名前空間ストアモジュールを使用する必要があります。
これらは、小道具からデータを受け取り、独自のローカルデータに変更を加え、簡単なイベントを発行する必要があります。ほとんどの場合、彼らはVuexストアの存在を知らないはずです。
他のUIコンポーネントにディスパッチすることが唯一の責任であるため、コンテナと呼ばれることもあります。
それで、結局のところ、2つの兄弟コンポーネント間でどのように通信する必要がありますか?
例で理解する方が簡単です。入力ボックスがあり、そのデータをアプリ全体(ツリー内のさまざまな場所にある兄弟)で共有し、バックエンドで永続化する必要があるとします。
最悪のシナリオから始めて、コンポーネントはpresentationとbusinessのロジックをミックスします。
// MyInput.vue
<template>
<div class="my-input">
<label>Data</label>
<input type="text"
:value="value"
:input="onChange($event.target.value)">
</div>
</template>
<script>
import axios from 'axios';
export default {
data() {
return {
value: "",
};
},
mounted() {
this.$root.$on('sync', data => {
this.value = data.myServerValue;
});
},
methods: {
onChange(value) {
this.value = value;
axios.post('http://example.com/api/update', {
myServerValue: value
})
.then((response) => {
this.$root.$emit('update', response.data);
});
}
}
}
</script>
これら2つの懸念を分離するには、コンポーネントをアプリ固有のコンテナーにラップし、プレゼンテーションロジックを汎用入力コンポーネントに保持する必要があります。
入力コンポーネントは再利用可能になり、バックエンドも兄弟も認識しません。
// MyInput.vue
// the template is the same as above
<script>
export default {
props: {
initial: {
type: String,
default: ""
}
},
data() {
return {
value: this.initial,
};
},
methods: {
onChange(value) {
this.value = value;
this.$emit('change', value);
}
}
}
</script>
アプリ固有のコンテナは、ビジネスロジックとプレゼンテーションコミュニケーションの橋渡しとなります。
// MyAppCard.vue
<template>
<div class="container">
<card-body>
<my-input :initial="serverValue" @change="updateState"></my-input>
<my-input :initial="otherValue" @change="updateState"></my-input>
</card-body>
<card-footer>
<my-button :disabled="!serverValue || !otherValue"
@click="saveState"></my-button>
</card-footer>
</div>
</template>
<script>
import { mapGetters, mapActions } from 'vuex';
import { NS, ACTIONS, GETTERS } from '@/store/modules/api';
import { MyButton, MyInput } from './components';
export default {
components: {
MyInput,
MyButton,
},
computed: mapGetters(NS, [
GETTERS.serverValue,
GETTERS.otherValue,
]),
methods: mapActions(NS, [
ACTIONS.updateState,
ACTIONS.updateState,
])
}
</script>
Vuexストアactionsはバックエンド通信を処理するため、ここのコンテナはaxiosとバックエンドについて知る必要はありません。
さて、v-on
イベントを使用して、親を介して兄弟間で通信できます。
Parent
|-List of items //sibling 1 - "List"
|-Details of selected item //sibling 2 - "Details"
Details
の要素をクリックしたときにList
コンポーネントを更新したいとします。
Parent
で:
テンプレート:
<list v-model="listModel"
v-on:select-item="setSelectedItem"
></list>
<details v-model="selectedModel"></details>
ここに:
v-on:select-item
これはイベントであり、List
コンポーネントで呼び出されます(以下を参照)。setSelectedItem
は、Parent
を更新するselectedModel
のメソッドです。JS:
//...
data () {
return {
listModel: ['a', 'b']
selectedModel: null
}
},
methods: {
setSelectedItem (item) {
this.selectedModel = item //here we change the Detail's model
},
}
//...
List
で:
テンプレート:
<ul>
<li v-for="i in list"
:value="i"
@click="select(i, $event)">
<span v-text="i"></span>
</li>
</ul>
JS:
//...
data () {
return {
selected: null
}
},
props: {
list: {
type: Array,
required: true
}
},
methods: {
select (item) {
this.selected = item
this.$emit('select-item', item) // here we call the event we waiting for in "Parent"
},
}
//...
ここに:
this.$emit('select-item', item)
は親のselect-item
を介して直接アイテムを送信します。そして、親はそれをDetails
ビューに送信します特に.sync
が非推奨となったVueの通常の通信パターンを「ハッキング」したい場合に通常行うことは、コンポーネント間の通信を処理する簡単なEventEmitterを作成することです。私の最新プロジェクトの1つから:
import {EventEmitter} from 'events'
var Transmitter = Object.assign({}, EventEmitter.prototype, { /* ... */ })
このTransmitter
オブジェクトを使用すると、任意のコンポーネントで実行できます。
import Transmitter from './Transmitter'
var ComponentOne = Vue.extend({
methods: {
transmit: Transmitter.emit('update')
}
})
「受信」コンポーネントを作成するには:
import Transmitter from './Transmitter'
var ComponentTwo = Vue.extend({
ready: function () {
Transmitter.on('update', this.doThingOnUpdate)
}
})
繰り返しますが、これは本当に特定の用途向けです。アプリケーション全体をこのパターンに基づかず、代わりにVuex
のようなものを使用してください。