Реактивность в Vue 3 представляет новые способы структурирования приложения
Vuex — это потрясающая библиотека управления состояниями. Она проста и хорошо интегрируется с Vue. Зачем кому-то уходить от Vuex? Причина может быть в том, что предстоящий релиз Vue 3 раскроет заложенную в ее основе систему реактивности и предложит новые способы структурирования приложения. Новая система реактивности настолько мощная, что это можно использовать для централизованного управления состояниями.
. . .
Нужно ли вам общее состояние?
Бывают обстоятельства, когда обмен данными между несколькими компонентами становится настолько сложным, что вам необходимо централизованное управление состоянием. К таким обстоятельствам относятся:
Несколько компонентов, использующих одни и те же данные.
Несколько корневых элементов (roots) имеющих доступ к данным.
Глубокая вложенность компонентов.
Если ни один из вышеперечисленных случаев не является подходящим, легко определить, нужно вам это или нет. Не нужно.
Как быть, если у вас есть один из этих случаев? Прямым ответом будет использование Vuex. Это проверенное решение, которое отлично справляется со своей задачей.
Что, если вы не хотите добавлять еще одну зависимость или считаете настройку слишком сложной? Вместе с API Composition новая версия Vue 3 может решить эти проблемы с помощью своих встроенных методов.
. . .
Новое решение
Общее состояние должно соответствовать двум критериям:
Реактивность: Когда состояние изменяется, компоненты, использующие его, также должны обновляться.
Доступность: К состоянию можно получить доступ в любом из компонентов.
. . .
Реактивность
Vue 3 раскрывает свою систему реактивности через множество функций. Вы можете создать реактивную переменную с помощью функции reactive
(альтернативой может быть функция ref
):
import { reactive } from 'vue';
export const state = reactive({ counter: 0 });
Объект, возвращаемый из функции reactive
, является объектом Proxy
, который может отслеживать изменения своих свойств. При использовании в шаблоне компонент рендерится заново при каждом изменении значения реактивной функции.
<template>
<div>{{ state.counter }}</div>
<button type="button" @click="state.counter++">Increment</button>
</template>
<script>
import { reactive } from 'vue';
export default {
setup() {
const state = reactive({ counter: 0 });
return { state };
}
};
</script>
Доступность
Приведенный выше пример прекрасно подходит для одного компонента, но при этом другие компоненты не могут получить доступ к состоянию. Чтобы решить эту проблему, вы можете сделать любое значение доступным внутри приложения Vue 3 с помощью методов provide
и inject
.
import { reactive, provide, inject } from 'vue';
export const stateSymbol = Symbol('state');
export const createState = () => reactive({ counter: 0 });
export const useState = () => inject(stateSymbol);
export const provideState = () => provide(
stateSymbol,
createState()
);
Когда вы передаете Symbol в качестве ключа и значение в метод provide
, оно будет доступно для любого дочернего компонента через метод inject
. Ключ использует одно и то же имя Symbol при предоставлении и получении значения.
Таким образом, если вы предоставите значение в самом верхнем компоненте, оно будет доступно во всех компонентах. В качестве альтернативы вы также можете вызвать provide
в главном экземпляре приложения.
import { createApp, reactive } from 'vue';
import App from './App.vue';
import { stateSymbol, createState } from './store';
const app = createApp(App);
app.provide(stateSymbol, createState());
app.mount('#app');
<script>
import { useState } from './state';
export default {
setup() {
return { state: useState() };
}
};
</script>
Обеспечение надежности
Приведенное выше решение работает, но имеет недостаток: вы не знаете, кто и что изменяет. Состояние может быть изменено напрямую, и здесь нет никаких ограничений.
Можно сделать состояние защищенным, обернув его функцией readonly
. Она покрывает переданную переменную в объекте Proxy
, который предотвращает любые изменения (выдает предупреждение, когда вы пытаетесь это сделать). Мутации могут обрабатываться отдельными функциями, имеющими доступ к хранилищу с возможностью записи.
import { reactive, readonly } from 'vue';
export const createStore = () => {
const state = reactive({ counter: 0 });
const increment = () => state.counter++;
return { increment, state: readonly(state) };
}
Внешний мир будет иметь доступ только к состоянию, предназначенному для чтения, и только экспортируемые функции могут изменять состояние, разрешенное для записи.
По защите состояния от нежелательных изменений новое решение сравнимо с Vuex.
Резюме
Используя систему реактивности и механизм внедрения зависимостей Vue 3, мы перешли от локального состояния к централизованному управлению состоянием, которое может заменить Vuex в небольших приложениях.
У нас есть объект состояния, который доступен только для чтения и реагирует на изменения в шаблонах. Состояние может быть изменено только с помощью специальных методов, таких как команды/мутации в Vuex. Вы можете определить дополнительные геттеры с помощью функции computed
.
Vuex имеет больше возможностей, таких как обработка модулей, но порой нам это не нужно.
Если вы хотите взглянуть на Vue 3 и попробовать этот подход к управлению состоянием, посмотрите на мою интерактивную среду Vue 3.
Все коды на изображениях для копирования доступны здесь.
Материал подготовлен в рамках курса «Vue.js разработчик».
Также приглашаем всех желающих на открытый урок «DatePicker». Разберем DatePicker, а именно:
— создадим библиотеку компонентов, чтобы получать DatePicker-ы c нужными параметрами;
— реализуем передачу данных через Provide/Inject;
— продемонстрируем композицию объектов черезextend
;
— создадим механизм стилизации через подключаемые темы.
Комментарии (5)
rudinandrey
28.07.2021 21:23Сегодня попробовал Pinia. Ну вполне интересно. Можно и в Composition и Option. Для меня особенно актуально можно несколько сторов.
YSpektor
29.07.2021 08:49Все же считаю, что компоненты должны быть ответсвенными только за презентацию и обработку пользовательского ввода. Управление и хранение состояния должно быть делегировано другим сущностям
simple_mortal
29.07.2021 08:52Для большого количества данных всё же без вьюекса, наверное, сложно обойтись. Просто потому что структура, модульность и соглашение о том, как и что мутировать. Для небольших массивов данных во вью2 я прекрасно обхожусь Vue.observable. Просто держу файлик, из которого экспортирую реактивный объект state и не реактивный mutations, который этот стейт мутирует. Ди сама дока говорит о том, что Vue.observable может использоваться как альтернатива vuex.
It can also be used as a minimal, cross-component state store for simple scenarios
george3
Там косяк с этими provide/inject : inject можно вызвать только из setup. Если писать без setup (смысла не несет для меня), то низзя. Кроме того залочили прередачу глобов через Vue.propotype. Фиг с ними, сделал обмен мимо их неудобных тулз. Не люблю тупые ограничения.
eshimischi
Неизвестно с чего вы взяли, что это косяк.. Из документации к Vue 3:
См в документе: https://v3.ru.vuejs.org/ru/guide/component-provide-inject.html#%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D0%B0-%D1%81-%D1%80%D0%B5%D0%B0%D0%BA%D1%82%D0%B8%D0%B2%D0%BD%D0%BE%D1%81%D1%82%D1%8C%D1%8E
Про реактивный provide/inject: https://v3.ru.vuejs.org/ru/guide/composition-api-provide-inject.html#provide-inject