Типи зв’язку
При розробці програми Vue (або насправді будь-якого додатка на основі компонентів) існують різні типи комунікацій, які залежать від того, з якими питаннями ми маємо справу, і вони мають свої канали зв'язку.
Бізнес-логіка: стосується всього конкретного для вашої програми та його мети.
Логіка презентації: все, з чим користувач взаємодіє або є результатом взаємодії користувача.
Ці два питання пов'язані з цими типами спілкування:
- Стан програми
- Батько-дитина
- Дитина-батько
- Брати і сестри
Кожен тип повинен використовувати правильний канал зв'язку.
Канали зв'язку
Канал - це вільний термін, який я буду використовувати для позначення конкретних реалізацій для обміну даними навколо програми Vue.
Реквізит: логіка презентації батько-дитина
Найпростіший канал зв'язку у Vue для прямого спілкування батько-дитина . Він здебільшого повинен використовуватися для передачі даних, що стосуються логіки презентації, або обмеженого набору даних вниз по ієрархії.
Реферати та методи: Анти-шаблон презентації
Коли немає сенсу використовувати опори, щоб дозволити дитині обробляти події від батьків, налаштування ref
на дочірній компонент та виклик його методів просто чудово.
Не робіть цього, це анти-шаблон. Перегляньте свою архітектуру компонентів та потік даних. Якщо ви хочете зателефонувати на метод дочірнього компонента від батьків, певно, настав час підняти стан або розглянути інші способи, описані тут або в інших відповідях.
Події: логіка презентації дитина-батько
$emit
і $on
. Найпростіший канал зв'язку для прямого спілкування дитина-батько. Знову ж таки, слід використовувати для логіки презентації.
Автобус події
Більшість відповідей дають хороші альтернативи шині подій, яка є одним із каналів зв'язку, доступних для віддалених компонентів, або будь-що насправді.
Це може стати корисним при передачі реквізитів по всьому місцю далеко вниз до глибоко вкладених дочірніх компонентів, причому майже ніяких інших компонентів вони не потребують між ними. Економно використовуйте для ретельно підібраних даних.
Будьте уважні: Подальше створення компонентів, які прив'язуються до шини подій, буде пов'язано не один раз - це призведе до запуску декількох обробників та протікань. Я особисто ніколи не відчував потреби в шині подій у всіх програмах на одній сторінці, які я розробляв у минулому.
Далі показано, як проста помилка призводить до витоку, коли Item
компонент все ще спрацьовує, навіть якщо його видалено з DOM.
// 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/vue@2.5.17/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 - це шлях з Vue для управління державою . Він пропонує набагато більше, ніж просто події, і він готовий до повномасштабного застосування.
А тепер ви запитаєте :
[S] Чи можу я створити магазин vuex для кожного другорядного спілкування?
Він дійсно світить, коли:
- робота з вашою бізнес-логікою,
- спілкування із заднім числом (або будь-яким шаром збереження даних, наприклад локальним сховищем)
Таким чином, ваші компоненти можуть дійсно зосередитись на речах, керуючи інтерфейсами користувача.
Це не означає, що ви не можете використовувати його для логіки компонентів, але я застосував би цю логіку до модуля Vuex, розміщеного на імена, лише з необхідним глобальним станом інтерфейсу користувача.
Щоб не стикатися з великим безладом всього глобального стану, магазин повинен бути розділений на кілька модулів, що мають простір імен.
Типи компонентів
Для того, щоб оркеструвати всі ці комунікації та полегшити повторну використання, ми повинні розглядати компоненти як два різних типи.
- Контейнери для конкретних програм
- Родові компоненти
Знову ж таки, це не означає, що загальний компонент слід використовувати повторно або що контейнер, який використовується для додатків, не можна повторно використовувати, але вони несуть різні обов'язки.
Контейнери для конкретних програм
Це просто прості компоненти Vue, які обгортають інші компоненти Vue (загальні або інші спеціальні контейнери). Тут має відбуватися спілкування магазину Vuex, і цей контейнер повинен спілкуватися за допомогою інших простих засобів, таких як реквізити та слухачі подій.
У цих контейнерах навіть не може бути вбудованих елементів DOM і дозволяти загальним компонентам мати справу з шаблонуванням та взаємодією користувачів.
Обсяг яким - то чином events
або stores
видимість для сибсов компонентів
Тут відбувається скопінг. Більшість компонентів не знають про магазин, і цей компонент повинен (в основному) використовувати один просторовий модуль магазину з обмеженим набором getters
і actions
застосовувати разом із наданими помічниками Vuex, що зв'язують .
Родові компоненти
Вони повинні отримувати свої дані від реквізиту, вносити зміни до власних локальних даних та випробовувати прості події. Здебільшого вони не повинні знати, що магазин Vuex взагалі існує.
Їх також можна назвати контейнерами, оскільки їх єдиною відповідальністю може бути відправлення на інші компоненти інтерфейсу.
Близький зв’язок
Отже, після всього цього, як нам спілкуватися між двома компонентами братів і сестер?
Це легше зрозуміти на прикладі: скажімо, у нас є поле для введення даних, і його дані слід обмінюватись у додатку (братів і сестер у різних місцях дерева) та зберігатись із доповненням.
Починаючи з найгіршого випадку , наш компонент змішав би презентацію та бізнес- логіку.
// 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>
Щоб розділити ці дві проблеми, ми повинні загортати наш компонент у контейнер для конкретного додатка та зберігати логіку презентації у нашому загальному компоненті введення.
Наш вхідний компонент тепер багаторазовий і не знає ні про вихід, ні про побратимів.
// 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 дії мати справу з серверної зв'язку, наш контейнер тут не потрібно знати про Вардар і внутрішньому інтерфейсі.
$emit
поєднується зv-model
для емуляції.sync
. Я думаю, ти повинен піти шляхом