Обзор
В Vue.js 2.x model.sync
будет объявлен устаревшим .
Итак, каков правильный способ взаимодействия между соседними компонентами в Vue.js 2.x ?
Задний план
Насколько я понимаю, Vue 2.x, предпочтительный метод для связи между братьями и сестрами - использовать хранилище или шину событий .
По словам Эвана (создателя Vue):
Также стоит упомянуть, что «передача данных между компонентами» - это вообще плохая идея, потому что в конце концов поток данных становится неотслеживаемым и очень трудным для отладки.
Если часть данных должна совместно использоваться несколькими компонентами, предпочтите глобальные хранилища или Vuex .
И:
.once
и.sync
устарели. Опоры теперь всегда в одну сторону. Чтобы вызвать побочные эффекты в родительской области, компоненту необходимо явноemit
явить событие, а не полагаться на неявную привязку.
Итак, Эван предлагает использовать $emit()
и $on()
.
Проблемы
Что меня беспокоит:
- У каждого
store
иevent
есть глобальная видимость (поправьте меня, если я ошибаюсь); - Слишком расточительно создавать новый магазин для каждого незначительного сообщения;
То , что я хочу , это какое - то рамка events
или stores
видимость для сибсов компонентов. (Или, возможно, я не понял вышеупомянутую идею.)
Вопрос
Итак, каков правильный способ связи между одноуровневыми компонентами?
источник
$emit
в сочетании сv-model
подражать.sync
. Я думаю, вам стоит пойти по путиОтветы:
В Vue 2.0 я использую механизм eventHub, как показано в документации .
Определите централизованный центр событий.
Теперь в вашем компоненте вы можете генерировать события с помощью
И слушать, как ты
Обновление См. Ответ @alex , в котором описывается более простое решение.
источник
this.$root.$emit()
иthis.$root.$on()
Вы даже можете сделать его короче и использовать корневой
Vue
экземпляр в качестве глобального концентратора событий:Компонент 1:
Компонент 2:
источник
Типы общения
При разработке приложения Vue (или, фактически, любого приложения на основе компонентов) существуют разные типы связи, которые зависят от того, с какими проблемами мы имеем дело, и у них есть свои собственные каналы связи.
Бизнес-логика: относится ко всему, что связано с вашим приложением и его целью.
Логика представления: все, с чем взаимодействует пользователь или что является результатом взаимодействия с пользователем.
Эти две проблемы связаны с этими типами общения:
Каждый тип должен использовать правильный канал связи.
Каналы связи
Канал - это общий термин, который я буду использовать для обозначения конкретных реализаций для обмена данными в приложении Vue.
Реквизиты: логика представления родитель-ребенок
Самый простой канал связи в Vue для прямого общения родителей и детей . В основном его следует использовать для передачи данных, относящихся к логике представления или ограниченному набору данных вниз по иерархии.
Ссылки и методы: Презентационный антипаттерн
Когда нет смысла использовать опору, позволяющую дочернему элементу обрабатывать событие от родителя, настройкаref
дочернего компонента и вызов его методов - это нормально.Не делай этого, это антипаттерн. Переосмыслите архитектуру компонентов и поток данных. Если вы обнаружите, что хотите вызвать метод дочернего компонента из родительского, вероятно, пора поднять состояние или рассмотреть другие способы, описанные здесь или в других ответах.
События: логика представления дочернего и родительского элементов
$emit
и$on
. Самый простой канал связи для прямого общения Родитель-Ребенок. Опять же, следует использовать для логики представления.Автобус событий
Большинство ответов дают хорошие альтернативы для шины событий, которая является одним из каналов связи, доступных для удаленных компонентов, или чего-то еще.
Это может стать полезным при передаче props повсюду, начиная с самых глубоко вложенных дочерних компонентов, и почти никаким другим компонентам они не нужны. Используйте осторожно для тщательно отобранных данных.
Будьте осторожны: последующее создание компонентов, которые привязываются к шине событий, будет связано более одного раза, что приведет к срабатыванию нескольких обработчиков и утечкам. Я лично никогда не чувствовал необходимости в шине событий во всех одностраничных приложениях, которые я проектировал в прошлом.
Ниже показано, как простая ошибка приводит к утечке, когда
Item
компонент все еще запускается, даже если он удален из DOM.Показать фрагмент кода
Не забудьте удалить слушателей в
destroyed
ловушке жизненного цикла.Централизованный магазин (Бизнес-логика)
Vuex - это способ использовать Vue для управления состоянием . Он предлагает гораздо больше, чем просто события, и готов к полномасштабному применению.
А теперь вы спрашиваете :
Он действительно сияет, когда:
Таким образом, ваши компоненты могут действительно сосредоточиться на том, чем они должны быть, на управлении пользовательскими интерфейсами.
Это не означает, что вы не можете использовать его для логики компонентов, но я бы применил эту логику к модулю Vuex с пространством имен только с необходимым глобальным состоянием пользовательского интерфейса.
Чтобы избежать большого беспорядка всего в глобальном состоянии, хранилище следует разделить на несколько модулей с именами.
Типы компонентов
Чтобы организовать все эти коммуникации и упростить повторное использование, мы должны рассматривать компоненты как два разных типа.
Опять же, это не означает, что общий компонент следует повторно использовать или что конкретный контейнер приложения нельзя повторно использовать, но у них разные обязанности.
Контейнеры для конкретных приложений
Это простой компонент Vue, который обертывает другие компоненты Vue (общие или другие контейнеры, специфичные для приложения). Здесь должно происходить взаимодействие хранилища Vuex, и этот контейнер должен взаимодействовать с помощью других более простых средств, таких как реквизиты и прослушиватели событий.
Эти контейнеры могут вообще не иметь собственных элементов DOM и позволять универсальным компонентам обрабатывать шаблоны и взаимодействие с пользователем.
Вот где происходит определение объема работ. Большинство компонентов не знают о хранилище, и этот компонент должен (в основном) использовать один модуль хранилища с пространством имен с ограниченным набором
getters
иactions
применяться с предоставленными помощниками привязки Vuex .Общие компоненты
Они должны получать свои данные от props, вносить изменения в свои локальные данные и генерировать простые события. В большинстве случаев они не должны знать, что магазин Vuex вообще существует.
Их также можно назвать контейнерами, поскольку их единственная ответственность может заключаться в отправке другим компонентам пользовательского интерфейса.
Братское общение
Итак, после всего этого, как мы должны общаться между двумя родственными компонентами?
Это легче понять на примере: допустим, у нас есть поле ввода, и его данные должны совместно использоваться в приложении (братья и сестры в разных местах в дереве) и сохраняться с помощью серверной части.
Начиная с наихудшего сценария , наш компонент будет смешивать презентацию и бизнес- логику.
Чтобы разделить эти две проблемы, мы должны заключить наш компонент в контейнер для конкретного приложения и сохранить логику представления в нашем общем компоненте ввода.
Наш компонент ввода теперь можно использовать повторно и не знает ни о серверной части, ни о братьях и сестрах.
Контейнер, специфичный для нашего приложения, теперь может быть мостом между бизнес-логикой и презентацией.
Так как магазин Vuex действия иметь дело с серверной связи, наш контейнер здесь не нужно знать о Вардар и внутреннем интерфейсе.
источник
Хорошо, мы можем общаться между братьями и сестрами через родителя, используя
v-on
события.Предположим, что мы хотим обновить
Details
компонент, когда мы щелкаем какой-либо элементList
.в
Parent
:Шаблон:
Вот:
v-on:select-item
это событие, которое будет вызываться вList
компоненте (см. ниже);setSelectedItem
этоParent
метод обновленияselectedModel
;JS:
В
List
:Шаблон:
JS:
Вот:
this.$emit('select-item', item)
отправит элементselect-item
напрямую в родительский. И родитель отправит его вDetails
представлениеисточник
Что я обычно делаю, если хочу «взломать» нормальные шаблоны взаимодействия в Vue, особенно теперь, которые
.sync
устарели, - это создать простой EventEmitter, который обрабатывает взаимодействие между компонентами. Из одного из моих последних проектов:Transmitter
Затем с помощью этого объекта вы можете в любом компоненте:И чтобы создать «принимающий» компонент:
Опять же, это действительно для конкретных целей. Не основывайте все свое приложение на этом шаблоне, используйте
Vuex
вместо этого что-нибудь вроде .источник
vuex
, но опять же, должен ли я создавать магазин vuex для каждого второстепенного сообщения?vuex
да, дерзайте. Используй это.Как вести себя при общении между братьями и сестрами, зависит от ситуации. Но сначала я хочу подчеркнуть, что подход глобальной шины событий уходит в Vue 3 . См. Этот RFC . Поэтому я решил написать новый ответ.
Наименьший образец общего предка (или «LCA»)
В простых случаях я настоятельно рекомендую использовать шаблон «Самый низкий общий предок» (также известный как «данные вниз, события вверх»). Этот шаблон легко читать, внедрять, тестировать и отлаживать.
По сути, это означает, что если два компонента нуждаются в взаимодействии, поместите их общее состояние в ближайший компонент, который является общим предком. Передайте данные от родительского компонента к дочернему компоненту через реквизиты и передайте информацию от дочернего к родительскому, испуская событие (см. Пример этого внизу этого ответа).
В качестве надуманного примера в приложении электронной почты, если компонент «Кому» должен взаимодействовать с компонентом «тело сообщения», состояние этого взаимодействия может находиться в их родительском элементе (возможно, в вызываемом компоненте
email-form
). У вас может быть опора вemail-form
вызываемом,addressee
чтобы тело сообщения могло автоматически добавлятьсяDear {{addressee.name}}
к электронной почте на основе адреса электронной почты получателя.LCA становится обременительной, если коммуникация проходит на большие расстояния с большим количеством посредников. Я часто рекомендую коллегам прочитать этот отличный пост в блоге . (Игнорируйте тот факт, что в его примерах используется Ember; его идеи применимы во многих фреймворках пользовательского интерфейса.)
Шаблон контейнера данных (например, Vuex)
В сложных случаях или ситуациях, когда в общении между родителями и потомками участвует слишком много посредников, используйте Vuex или аналогичную технологию контейнеров данных. При необходимости используйте модули с именами .
Например, было бы разумно создать отдельное пространство имен для сложной коллекции компонентов с множеством взаимосвязей, таких как полнофункциональный компонент календаря.
Шаблон публикации / подписки (шина событий)
Если шаблон шины событий (или «публикация / подписка») более подходит для ваших нужд, основная команда Vue теперь рекомендует использовать стороннюю библиотеку, такую как mitt . (См. RFC, указанный в параграфе 1.)
Бонусные рассылки и код
Вот базовый пример решения Lowest Common Ancestor для общения между братьями и сестрами, проиллюстрированный в игре « Ударь крота» .
Наивный подход может заключаться в том, чтобы думать: «Родинка 1 должна сказать родинке 2, чтобы она появилась после того, как ее ударили». Но Vue не одобряет такой подход, так как он хочет, чтобы мы думали в терминах древовидной структуры .
Наверное, это очень хорошо. Нетривиальное приложение, в котором узлы взаимодействуют напрямую друг с другом через деревья DOM, было бы очень сложно отлаживать без какой-либо системы учета (например, Vuex). Вдобавок ко всему, компоненты, которые используют «данные вниз, события вверх», как правило, демонстрируют низкую взаимосвязь и высокую возможность повторного использования - оба очень желательных качества, которые помогают масштабировать большие приложения.
В этом примере при ударе крота происходит событие. Компонент игрового менеджера решает, какое новое состояние приложения, и, таким образом, родственный крот знает, что делать, неявно после повторного рендеринга Vue. Это несколько банальный пример «низшего общего предка».
источник