… или знакомство с плагинами Vue JS на примере интегрированной шины событий
Несколько слов о.
Всем привет! Сразу оговорюсь.
Я очень люблю VueJS, активно пишу на нем уже более 2 лет и не думаю, что разработка на нем может причинить сколько-нибудь существенную боль :) С другой стороны, мы всегда пытаемся найти универсальные решения, которые помогут нам тратить меньше времени на механическую работу и больше на то, что действительно интересно.
Иногда решение оказывается особенно удачным.
Я хочу поделиться с вами одним из них.
10 строк, о которых пойдет речь (спойлер: в конце будет еще немного), родились в процессе работы над проектом Cloud Blue — Connect, который представляет собой достаточно большое приложение с 400+ компонентами.
Найденное нами решение уже интегрировано в различные точки системы и более полугода ни разу не требовало каких-либо изменений, поэтому его смело можно считать успешно протестированным на стабильность.
И последнее.
Прежде чем перейти непосредственно к решению, я хотел бы более подробно остановиться на трех типах взаимодействия между компонентами Vue: принципах однонаправленного потока, шаблоне хранилища и шине событий.
Если это объяснение для вас лишнее (или скучное), переходите сразу к разделу с решением — там все максимально кратко и технично.
Немного о том, как компоненты Vue взаимодействуют друг с другом
Пожалуй, первый вопрос, который возникает у человека, написавшего свой первый компонент, касается того, как он будет получать данные для работы и как, в свою очередь, передавать полученные данные «вовне».Принцип взаимодействия, принятый во фреймворке Vue JS, называется.
Однонаправленный поток данных
Короче говоря, этот принцип звучит как «свойства вниз, события вверх».То есть для получения данных извне («сверху») мы регистрируем внутри компонента специальное свойство, в которое фреймворк при необходимости записывает наши данные, полученные «извне».
Чтобы передать данные «вверх», внутри компонента в нужном месте, мы вызываем специальный метод фреймворка $emit, который передает наши данные обработчику родительского компонента.
При этом в Vue JS мы не можем просто «транслировать» событие вверх на неограниченную глубину (как, например, в Angular 1.x).
Он «всплывает» только на один уровень, до непосредственного родителя.
То же самое касается и событий.
Чтобы передать их на следующий уровень, для каждого из них нам также необходимо прописать специальный интерфейс — свойства и события, которые будут передавать наше «сообщение» дальше.
Его можно охарактеризовать как офисное здание, в котором работники могут перемещаться только со своего этажа на соседние – один вверх, другой вниз.
Итак, чтобы передать «документ на подпись» с пятого этажа на второй, вам понадобится цепочка из трех рабочих, которые доставят его с пятого этажа на второй, а затем еще трое, которые доставят его обратно на пятый.
.
«Но это неудобно!» Конечно, это не всегда удобно с точки зрения разработки, но, просматривая код каждого компонента, мы можем увидеть, что и кому он передает. Нам не нужно держать в голове всю структуру приложения, чтобы понять, находится ли наш компонент «на пути» события или нет. Мы можем видеть это из родительского компонента.
Хотя преимущества этого подхода очевидны, есть и очевидные недостатки, а именно высокая степень связанности компонентов.
Проще говоря, чтобы мы могли поместить компонент в структуру, нам нужно окружить его необходимыми интерфейсами для управления его состоянием.
Чтобы уменьшить эту связь, чаще всего используются «инструменты управления состоянием».
Пожалуй, самый популярный инструмент для Vue — это…
Вуэкс (магазины)
Продолжая аналогию с офисным зданием, магазин Vuex — это внутренняя почтовая служба.Представим, что на каждом этаже офиса есть окно для выдачи и получения посылок.
На пятом этаже сдают на подпись документ № 11, а на втором периодически спрашивают: «Есть ли документы на подписьЭ», подписывают имеющиеся и отдают обратно.
На пятом тоже спрашивают: «Есть подписанныеЭ» При этом сотрудники могут переезжать на другие этажи или в другие помещения – принцип работы не изменится, пока работает почта.
Примерно так работает шаблон Store. С помощью интерфейса Vuex регистрируется и настраивается некое глобальное хранилище данных, на которое подписываются компоненты.
При этом не важно, с какого уровня какой структуры произошел запрос, магазин всегда предоставит необходимую информацию.
Казалось бы, все проблемы уже решены.
Но в какой-то момент нашего метафорического здания один сотрудник хочет позвать другого на обед… или сообщить о какой-то ошибке.
И здесь все становится странным.
Само сообщение не требует передачи как таковой.
Но чтобы пользоваться почтой, нужно что-то отправить.
Затем наши сотрудники придумывают код. Один зеленый шарик – пойдем обедать, два красных кубика – произошла ошибка приложения Е-981273, три желтые монетки – проверьте почту и так далее.
Как вы могли догадаться, я использую эту неуклюжую метафору для описания ситуаций, когда нам нужно убедиться, что наш компонент реагирует на событие, происходящее в другом компоненте, который сам по себе не имеет никакого отношения к потоку данных.
Сохранение нового элемента завершено — необходимо повторно запросить коллекцию.
Произошла ошибка 403 Unauthorized — пользователю необходимо выйти из системы и так далее.
Обычная (и далеко не лучшая) практика в этом случае — создание флагов внутри хранилища или косвенная интерпретация хранимых данных и их изменений.
Это быстро приводит к загрязнению как самого магазина, так и логики компонентов вокруг него.
На этом этапе мы начинаем думать о том, как передавать события напрямую, минуя всю цепочку компонентов.
И немного погуглив или покопавшись в документации, мы натыкаемся на закономерность.
Автобус событий
С технической точки зрения шина событий — это объект, который позволяет запускать «событие» с помощью одного специального метода и подписываться на него с помощью другого.Другими словами, при подписке на событие «eventA» этот объект сохраняет внутри своей структуры переданную функцию-обработчик, которую он будет вызывать при вызове метода запуска с ключом «eventA» где-то в приложении.
Чтобы подписать или запустить его, просто получите к нему доступ через импорт или ссылку, и все готово.
Образно говоря, в нашем «здании» автобус — это общие чаты в мессенджере.
Компоненты подписываются на «общий чат», в который другие компоненты отправляют сообщения.
Как только в «чате» появится «сообщение», на которое подписался компонент, будет запущен обработчик.
Существует много разных способов создания шины событий.
Вы можете написать его самостоятельно, а можете использовать готовые решения — тот же RxJS, который предоставляет огромный функционал для работы с целыми потоками событий.
Но чаще всего при работе с VueJS используют, как ни странно, сам VueJS. Экземпляр Vue, созданный с помощью конструктора (новый Vue()), предоставляет красивый и лаконичный интерфейс событий, описанный в официальной документации.
Здесь мы подходим к следующему вопросу.
Что мы хотим?
И мы хотим встроить в наше приложение шину событий.Но у нас есть два дополнительных требования:
- Он должен быть легко доступен в каждом компоненте.
Отдельный импорт в каждый из десятков компонентов нам кажется излишним.
- Он должен быть модульным.
Мы не хотим запоминать все имена событий, чтобы избежать ситуации, когда событие «созданный элемент» запускает обработчики во всем приложении.
Поэтому мы хотим иметь возможность легко выделить небольшой кусочек дерева компонентов в отдельный модуль и транслировать его события внутри него, а не за его пределами.
Давайте сначала зарегистрируем наш плагин.
Для этого прямо перед точкой инициализации нашего приложения Vue (перед вызовом Vue.$mount()) мы поместим следующий блок:
По сути, плагины Vue — это способ расширить функциональность фреймворка на уровне всего приложения.Vue.use({ install(vue) { }, });
Интерфейс плагина предоставляет несколько способов встраивания себя в компонент, но сегодня мы рассмотрим интерфейс миксина.
Этот метод принимает объект, который расширяет дескриптор каждого компонента перед началом его жизненного цикла в приложении.
(Код компонента, который мы пишем, — это не сам компонент, а описание его поведения и инкапсуляция определенной части логики, которая используется фреймворком на разных этапах его жизненного цикла.
Инициализация плагина находится вне жизненного цикла компонента.
, предшествующий ему, поэтому мы говорим «дескриптор», а не компонент, чтобы подчеркнуть, что в миксин-секцию плагина будет передан именно тот код, который написан в нашем файле, а не какая-то сущность, являющаяся продуктом фреймворка) .
Vue.use({
install(vue) {
vue.mixin({}); // <--
},
});
Именно этот пустой объект будет содержать расширения для наших компонентов.
Но сначала еще одна остановка.
В нашем случае мы хотим создать интерфейс доступа к шине на уровне каждого компонента.
Давайте добавим поле $broadcast в наш дескриптор; он будет хранить ссылку на наш автобус.
Для этого мы будем использовать Vue.prototype: Vue.use({
install(vue) {
vue.prototype.$broadcast = null; // <--
vue.mixin({});
},
});
Теперь нам нужно создать саму шину, но сначала давайте вспомним о требовании модульности и примем, что в дескрипторе компонента мы объявим новый модуль с полем «$module» с каким-то текстовым значением (он нам понадобится чуть позже).
).
Если в самом компоненте указано поле $module, мы создадим для него новую шину; если нет, мы передадим ссылку родительскому элементу через поле $parent. Обратите внимание, что поля дескриптора будут доступны нам через поле $options.
Разместим создание нашего автобуса на самом раннем этапе — в хуке beforeCreate. Vue.use({
install(vue) {
vue.prototype.$broadcast = null;
vue.mixin({
beforeCreate() { // <--
if (this.$options.$module) { // <--
Теги: #JavaScript #vue.js #vue #events-bus #beginners #vue-компоненты
-
Сомали
19 Oct, 24 -
История Логова Дракона
19 Oct, 24 -
Почему Google Использует Домен Duck.com?
19 Oct, 24 -
Используете Ли Вы Css-Фреймворки?
19 Oct, 24