Я вважаю, що розуміння мотивацій, що стоять за мутаціями та діями, дозволяє краще судити, коли використовувати, як і як. Це також звільняє програміста від тягаря невизначеності в ситуаціях, коли "правила" стають нечіткими. Трохи обміркувавши їхні цілі, я прийшов до висновку, що хоча напевно можуть бути неправильні способи використання дій та мутацій, я не думаю, що існує канонічний підхід.
Спершу спробуємо зрозуміти, чому ми навіть переживаємо або мутації, або дії.
Навіщо в першу чергу проходити через котельну плиту? Чому б не змінити стан безпосередньо в компонентах?
Строго кажучи, ви можете змінити state
безпосередньо свої компоненти. Це state
просто об’єкт JavaScript, і немає нічого магічного, що поверне зміни, які ви внесете до нього.
// Yes, you can!
this.$store.state['products'].push(product)
Однак, роблячи це, ви розпорошуєте свої мутації стану по всьому світу. Ви втрачаєте можливість просто відкрити один модуль, в якому знаходиться стан, і з першого погляду побачите, які операції можна застосувати до нього. Централізовані мутації вирішують це, хоча і ціною деяких котлових плит.
// so we go from this
this.$store.state['products'].push(product)
// to this
this.$store.commit('addProduct', {product})
...
// and in store
addProduct(state, {product}){
state.products.push(product)
}
...
Я думаю, що якщо ви заміните щось коротке на котельну, ви хочете, щоб і котла мала. Тому я припускаю, що мутації мають бути дуже тонкими обгортками навколо власних операцій над державою, майже не мають ділової логіки. Іншими словами, мутації мають використовуватися в основному як сетери.
Тепер, коли ви централізували свої мутації, ви маєте кращий огляд змін у своєму стані, і оскільки ваш інструментарій (vue-devtools) також знає про це місце, це робить налагодження простішим. Варто також пам’ятати, що багато плагінів Vuex не стежать за станом безпосередньо, щоб відстежувати зміни, вони швидше покладаються на мутації. Таким чином, "поза межами" зміни стану невідомі.
Отже mutations
, у actions
чому різниця?
Дії, як і мутації, також знаходяться в модулі магазину і можуть приймати state
об’єкт. Що означає, що вони також могли його безпосередньо мутувати. Тож який сенс мати обоє? Якщо ми вважаємо, що мутації мають бути малі та прості, то це означає, що нам потрібен альтернативний засіб для більш детальної бізнес-логіки. Дії - це засіб для цього. А оскільки, як ми встановили раніше, vue-devtools та плагіни знають про зміни через мутації, щоб залишатися послідовними, нам слід продовжувати використовувати мутації в наших діях. Крім того, оскільки дії маються на увазі, що всі вони охоплюють і що логіка, яку вони інкапсулюють, може бути асинхронною, має сенс, що Дії також з самого початку просто зробили б асинхронними.
Часто підкреслюється, що дії можуть бути асинхронними, тоді як мутації, як правило, ні. Ви можете вирішити бачити розмежування як вказівку на те, що мутації повинні використовуватися для чогось синхронного (і дії для будь-чого асинхронного); однак, ви зіткнетесь з деякими труднощами, якщо, наприклад, вам потрібно було зробити кілька мутацій (синхронно) або якщо вам потрібно було працювати з Getter зі своїх мутацій, оскільки функції мутації не отримують ні Геттерс, ні Мутації як аргументи ...
... що призводить до цікавого питання.
Чому мутації не отримують Геттерів?
Я ще не знайшов задовільної відповіді на це питання. Я бачив деякі пояснення основної команди про те, що я знайшов суперечки в кращому випадку. Якщо я підсумую їх використання, то Getters мають на увазі обчислювати (і часто кешовувати) розширення на стан. Іншими словами, вони в основному все ще є державою, хоча це вимагає певних обчислень, і вони, як правило, лише для читання. Так принаймні, як їх рекомендують використовувати.
Таким чином, запобігання мутаціям безпосередньо отримувати доступ до Getters означає, що зараз необхідна одна з трьох речей, якщо нам потрібно отримати доступ до колишнього функціоналу, запропонованого останнім: (1) або державні обчислення, що надаються Getter, дублюються десь доступними до мутації (неприємний запах) або (2) обчислене значення (або відповідний сам Геттер) передається як явний аргумент Мутації (фанки), або (3) сама логіка Геттера дублюється безпосередньо в межах мутації , без додаткової переваги кешування, передбаченого Getter (сморід).
Далі наведено приклад (2), який у більшості сценаріїв, з якими я стикався, здається "найменш поганим" варіантом.
state:{
shoppingCart: {
products: []
}
},
getters:{
hasProduct(state){
return function(product) { ... }
}
}
actions: {
addProduct({state, getters, commit, dispatch}, {product}){
// all kinds of business logic goes here
// then pull out some computed state
const hasProduct = getters.hasProduct(product)
// and pass it to the mutation
commit('addProduct', {product, hasProduct})
}
}
mutations: {
addProduct(state, {product, hasProduct}){
if (hasProduct){
// mutate the state one way
} else {
// mutate the state another way
}
}
}
Мені сказане здається не лише дещо заплутаним, але й дещо "витікаючим", оскільки частина коду, присутнього в дії, явно витікає із внутрішньої логіки мутації.
На мою думку, це є ознакою компромісу. Я вважаю, що дозволити мутаціям автоматично приймати Геттерс представляє певні проблеми. Це може бути або сама конструкція Vuex, або інструментарій (vue-devtools та ін.), Або підтримка деякої відсталої сумісності, або якась комбінація всіх заявлених можливостей.
У що я не вірю, що передача Геттерів своїм мутаціям - це обов'язково знак того, що ви щось робите не так. Я вважаю це просто "латанням" одного з недоліків рамки.