Что будет с VueX?

Опубликовано: 2022-03-10
Краткое резюме ↬ Vuex — это библиотека управления состоянием для приложений Vue, и основная команда Vue имеет большие планы по ее улучшению. Вот предварительный просмотр того, где они надеются взять его.

Vuex — это решение для управления состоянием в приложениях Vue. Следующая версия — Vuex 4 — проходит последние этапы перед официальным выпуском. Этот выпуск обеспечит полную совместимость с Vue 3, но не добавит новых функций. Хотя Vuex всегда был мощным решением и первым выбором для многих разработчиков для управления состоянием в Vue, некоторые разработчики надеялись увидеть больше решенных проблем рабочего процесса. Однако, пока Vuex 4 только выходит, Киа Кинг Исии (член основной команды Vue) рассказывает о своих планах относительно Vuex 5, и я так взволнован увиденным, что должен поделиться этим с вами. все. Обратите внимание, что планы Vuex 5 еще не завершены, поэтому некоторые вещи могут измениться до того, как Vuex 5 будет выпущен, но если в конечном итоге он будет в основном похож на то, что вы видите в этой статье, это должно значительно улучшить опыт разработчиков.

С появлением Vue 3 и его API-интерфейса люди начали искать простые альтернативы, созданные вручную. Например, You Might Not Need Vuex демонстрирует относительно простой, но гибкий и надежный шаблон для использования API композиции вместе с provide/inject для создания общих хранилищ состояний. Однако, как утверждает Габор в своей статье, эту (и другие альтернативы) следует использовать только в небольших приложениях, потому что в них отсутствуют все те вещи, которые не относятся непосредственно к коду: поддержка сообщества, документация, соглашения, хорошая интеграция Nuxt и разработчик. инструменты.

Последнее всегда было для меня одной из самых больших проблем. Браузерное расширение Vue devtools всегда было замечательным инструментом для отладки и разработки приложений Vue, и потеря инспектора Vuex с «путешествием во времени» была бы довольно большой потерей для отладки любых нетривиальных приложений.

Отладка Vuex с помощью Vue Devtools
Отладка Vuex с помощью Vue Devtools. (Большой превью)

К счастью, с Vuex 5 мы сможем не только съесть свой пирог, но и съесть его. Он будет больше похож на эти альтернативы API композиции, но сохранит все преимущества использования официальной библиотеки управления состоянием. Теперь давайте посмотрим, что изменится.

Еще после прыжка! Продолжить чтение ниже ↓

Определение магазина

Прежде чем мы сможем что-то сделать с хранилищем Vuex, нам нужно его определить. В Vuex 4 определение магазина будет выглядеть так:

 import { createStore } from 'vuex' export const counterStore = createStore({ state: { count: 0 }, getters: { double (state) { return state.count * 2 } }, mutations: { increment (state) { state.count++ } }, actions: { increment (context) { context.commit('increment') } } })

Каждое хранилище состоит из четырех частей: state хранит данные, getters дают вам вычисленное состояние, mutations используются для изменения состояния, а actions — это методы, которые вызываются извне хранилища для выполнения каких-либо действий, связанных с хранилищем. Обычно действия не просто совершают мутацию, как показано в этом примере. Вместо этого они используются для выполнения асинхронных задач, потому что мутации должны быть синхронными или они просто реализуют более сложные или многоэтапные функции. Действия также не могут изменять состояние сами по себе; они должны использовать мутатор. Так как же выглядит Vuex 5?

 import { defineStore } from 'vuex' export const counterStore = defineStore({ name: 'counter', state() { return { count: 0 } }, getters: { double () { return this.count * 2 } }, actions: { increment () { this.count++ } } })

Здесь следует отметить несколько изменений. Во-первых, вместо createStore мы используем defineStore . Эта разница незначительна, но она существует по семантическим причинам, которые мы рассмотрим позже. Далее нам нужно указать name для магазина, которое нам раньше не требовалось. В прошлом у модулей были собственные имена, но они не предоставлялись самим модулем; они были просто именем свойства, которому они были назначены родительским хранилищем, которое их добавило. Теперь модулей нет . Вместо этого каждый модуль будет отдельным магазином и будет иметь имя. Это имя используется реестром Vuex, о котором мы поговорим позже.

После этого нам нужно сделать state функцией, которая возвращает начальное состояние, а не просто устанавливает его в начальное состояние. Это похоже на параметр data для компонентов. Мы пишем getters очень похоже на то, как мы это делали в Vuex 4, но вместо того, чтобы использовать state в качестве параметра для каждого геттера, вы можете просто использовать this , чтобы получить состояние. Точно так же actions не нужно беспокоиться о передаваемом объекте context : они могут просто использовать this для доступа ко всему. Наконец, нет никаких mutations . Вместо этого мутации сочетаются с actions . Киа отметила, что слишком часто мутации просто становились простыми сеттерами, что делало их бессмысленно многословными, поэтому они удаляли их. Он не упомянул, можно ли изменять состояние непосредственно вне хранилища, но нам определенно разрешено и рекомендуется изменять состояние непосредственно из действия, а паттерн Flux осуждает прямое изменение состояния.

Примечание . Для тех, кто предпочитает API-интерфейс композиции, а не API-интерфейс параметров для создания компонентов, вы будете рады узнать, что существует также способ создания хранилищ аналогично использованию API-интерфейса композиции.

 import { ref, computed } from 'vue' import { defineStore } from 'vuex' export const counterStore = defineStore('counter', { const count = ref(0) const double = computed(() => count.value * 2) function increment () { count.value++ } return { count, double, increment } })

Как показано выше, имя передается в качестве первого аргумента для defineStore . Остальное выглядит как функция композиции для компонентов. Это даст точно такой же результат, как и в предыдущем примере, в котором использовался API опций.

Получение экземпляра магазина

В Vuex 4 многое изменилось по сравнению с Vuex 3, но я просто рассмотрю v4, чтобы не выйти из-под контроля. В v4, когда вы вызывали createStore , вы уже создали его экземпляр. Затем вы можете просто использовать его в своем приложении либо через app.use , либо напрямую:

 import { createApp } from 'vue' import App from './App.vue' // Your root component import store from './store' // The store definition from earlier const app = createApp(App) app.use(store) app.mount('#app') // Now all your components can access it via `this.$store` // Or you can use in composition components with `useStore()` // ----------------------------------------------- // Or use directly... this is generally discouraged import store from './store' store.state.count // -> 0 store.commit('increment') store.dispatch('increment') store.getters.double // -> 4

Это одна вещь, которую Vuex 5 делает немного сложнее, чем в версии 4. Каждое приложение теперь может получить отдельный экземпляр Vuex, что гарантирует, что каждое приложение может иметь отдельные экземпляры одних и тех же хранилищ без обмена данными между ними. Вы можете поделиться экземпляром Vuex, если хотите поделиться экземплярами магазинов между приложениями.

 import { createApp } from 'vue' import { createVuex } from 'vuex' import App from './App.vue' // Your root component const app = createApp(App) const vuex = createVuex() // create instance of Vuex app.use(vuex) // use the instance app.mount('#app')

Теперь все ваши компоненты имеют доступ к экземпляру Vuex. Вместо того, чтобы давать определение вашего магазина (ов) напрямую, вы затем импортируете их в компоненты, в которых хотите их использовать, и используете экземпляр Vuex для их создания и регистрации:

 import { defineComponent } from 'vue' import store from './store' export default defineComponent({ name: 'App', computed: { counter () { return this.$vuex.store(store) } } })

Вызов $vuex.store создает и регистрирует хранилище в экземпляре Vuex. С этого момента каждый раз, когда вы используете $vuex.store в этом магазине, он возвращает вам уже созданный экземпляр хранилища, а не создает его снова. Вы можете вызвать метод store непосредственно для экземпляра Vuex, созданного с помощью createVuex() .

Теперь ваш магазин доступен в этом компоненте через this.counter . Если вы используете API композиции для своего компонента, вы можете использовать useStore вместо this.$vuex.store :

 import { defineComponent } from 'vue' import { useStore } from 'vuex' // import useStore import store from './store' export default defineComponent({ setup () { const counter = useStore(store) return { counter } } })

Есть плюсы и минусы импорта хранилища непосредственно в компонент и его создания там. Это позволяет вам разделять код и лениво загружать хранилище только там, где это необходимо, но теперь это прямая зависимость, а не вставка родителем (не говоря уже о том, что вам нужно импортировать его каждый раз, когда вы хотите его использовать). Если вы хотите использовать внедрение зависимостей, чтобы предоставить его во всем приложении, особенно если вы знаете, что оно будет использоваться в корне приложения, где разделение кода не поможет, вы можете просто использовать provide :

 import { createApp } from 'vue' import { createVuex } from 'vuex' import App from './App.vue' import store from './store' const app = createApp(App) const vuex = createVuex() app.use(vuex) app.provide('store', store) // provide the store to all components app.mount('#app')

И вы можете просто внедрить его в любой компонент, где собираетесь его использовать:

 import { defineComponent } from 'vue' export default defineComponent({ name: 'App', inject: ['store'] }) // Or with Composition API import { defineComponent, inject } from 'vue' export default defineComponent({ setup () { const store = inject('store') return { store } } })

Я не в восторге от этой лишней многословности, но она более явная и более гибкая, что мне нравится. Этот тип кода обычно пишется один раз сразу в начале проекта, и затем он вас больше не беспокоит, хотя теперь вам нужно будет либо предоставлять каждый новый магазин, либо импортировать его каждый раз, когда вы хотите его использовать, но импорт или вставка модулей кода — это то, как мы обычно должны работать с чем-либо еще, поэтому это просто заставляет Vuex работать больше в соответствии с тем, как люди уже склонны работать.

Использование магазина

Помимо того, что я являюсь поклонником гибкости и нового способа определения хранилищ так же, как компонент с использованием API композиции, есть еще одна вещь, которая меня больше всего волнует: как используются хранилища. Вот как выглядит использование хранилища в Vuex 4.

 store.state.count // Access State store.getters.double // Access Getters store.commit('increment') // Mutate State store.dispatch('increment') // Run Actions

State , getters , mutations и actions обрабатываются по-разному с помощью разных свойств или методов. Преимущество этой явности в том, что я хвалил ранее, но эта явность на самом деле ничего нам не дает. И этот API становится сложнее использовать, когда вы используете модули с именами. Для сравнения, Vuex 5 работает именно так, как вы обычно надеетесь:

 store.count // Access State store.double // Access Getters (transparent) store.increment() // Run actions // No Mutators

Все — состояние, геттеры и действия — доступно непосредственно в корне хранилища, что упрощает его использование с гораздо меньшей многословностью и практически устраняет необходимость использования mapState , mapGetters , mapActions и mapMutations для API опций или для записи. дополнительные computed операторы или простые функции для составного API. Это просто делает магазин Vuex похожим на обычный магазин, который вы создали бы сами, но он получает все преимущества плагинов, инструментов отладки, официальной документации и т. д.

Составление магазинов

Последний аспект Vuex 5, который мы сегодня рассмотрим, — это компонуемость. Vuex 5 не имеет модулей с именами, которые доступны из одного хранилища. Каждый из этих модулей будет разделен на совершенно отдельный магазин. Это достаточно просто для компонентов: они просто импортируют любые хранилища, которые им нужны, запускают их и используют. Но что, если один магазин хочет взаимодействовать с другим магазином? В v4 пространство имен запутывает все это, поэтому вам нужно использовать пространство имен в вызовах commit и dispatch , использовать rootGetters и rootState а затем продвигаться вверх по пространствам имен, из которых вы хотите получить доступ к геттерам и состоянию. Вот как это работает в Vuex 5:

 // store/greeter.js import { defineStore } from 'vuex' export default defineStore({ name: 'greeter', state () { return { greeting: 'Hello' } } }) // store/counter.js import { defineStore } from 'vuex' import greeterStore from './greeter' // Import the store you want to interact with export default defineStore({ name: 'counter', // Then `use` the store use () { return { greeter: greeterStore } }, state () { return { count: 0 } }, getters: { greetingCount () { return `${this.greeter.greeting} ${this.count}' // access it from this.greeter } } })

В версии 5 мы импортируем хранилище, которое хотим использовать, затем регистрируем его с use , и теперь оно доступно во всем магазине с любым именем свойства, которое вы ему дали. Все еще проще, если вы используете вариацию API композиции определения магазина:

 // store/counter.js import { ref, computed } from 'vue' import { defineStore } from 'vuex' import greeterStore from './greeter' // Import the store you want to interact with export default defineStore('counter', ({use}) => { // `use` is passed in to function const greeter = use(greeterStore) // use `use` and now you have full access const count = 0 const greetingCount = computed(() => { return `${greeter.greeting} ${this.count}` // access it like any other variable }) return { count, greetingCount } })

Больше никаких модулей с пространством имен. Каждый магазин является отдельным и используется отдельно. Вы можете использовать use , чтобы сделать магазин доступным внутри другого магазина для их составления. В обоих примерах use — это в основном тот же механизм, что и vuex.store из предыдущего, и они гарантируют, что мы создаем экземпляры хранилищ с правильным экземпляром Vuex.

Поддержка машинописного текста

Для пользователей TypeScript одним из замечательных аспектов Vuex 5 является то, что упрощение упростило добавление типов ко всему. Уровни абстракции, которые в старых версиях Vuex делали почти невозможными, и прямо сейчас, с Vuex 4, они расширили наши возможности использования типов, но все еще слишком много ручной работы, чтобы получить достаточную поддержку типов, тогда как в v5 , вы можете поместить свои типы в строку, как вы надеетесь и ожидаете.

Заключение

Vuex 5 выглядит почти именно так, как я и, вероятно, многие другие надеялись, и я чувствую, что он не может появиться достаточно скоро. Он упрощает большую часть Vuex, устраняя некоторые умственные затраты, и становится более сложным или многословным только там, где добавляет гибкости. Оставляйте комментарии ниже о том, что вы думаете об этих изменениях и какие изменения вы могли бы внести вместо них или в дополнение. Или перейдите прямо к источнику и добавьте RFC (запрос комментариев) в список, чтобы узнать, что думает основная команда.