Автор: Майкл Тиссен
Переводчик: Front-end Xiaozhi
Источник: разработчик
Ставь лайк и смотри, поиск в WeChat【Переезд в мир】Обратите внимание на этого человека, который не имеет большого фабричного прошлого, но имеет восходящий и позитивный настрой. эта статья
GitHubGitHub.com/QQ449245884…Он был включен, статьи были классифицированы, и многие мои документы и учебные материалы были систематизированы.
Все говорили, что нет проекта для написания резюме, поэтому я помог вам найти проект, и это было с бонусом.【Учебник по строительству】.
Сначала рассмотрим вопрос: есть ли способ заполнить слот родительского компонента дочерним компонентом?
Недавно коллега задал мне этот вопрос, и ответ был прост: да. Но мое решение может сильно отличаться от того, что вы думаете, оно включает в себя сложную проблему архитектуры Vue, но также и очень интересную.
Почему эта проблема?
В нашем приложении у нас есть верхняя панель, содержащая различные кнопки, панель поиска и некоторые другие элементы управления. Он может немного отличаться в зависимости от страницы, на которой все находятся, поэтому нам нужен способ настроить его для каждой страницы отдельно.
Для этого мы хотим, чтобы каждая страница могла настроить панель действий. Выглядит просто, но вот проблема
Эта верхняя полоса (назовем ееActionBar) на самом деле является частью нашего основного макета, структурированного следующим образом:
<template>
<div>
<FullPageError />
<ActionBar />
<App />
</div>
</template>
Динамическая инъекция на основе страницы/маршрута, на котором вы находитесьAppпозиция.
мы можем использоватьActionBarНекоторый слот для настройки. Но как мы изAppуправлять этими слотами в компоненте?
определить проблему
Во-первых, лучше всего как можно более четко знать, какую проблему мы пытаемся решить.
Давайте посмотрим на компонент с одним дочерним элементом и одним слотом:
// Parent.vue
<template>
<div>
<Child />
<slot />
</div>
</template>
Мы можем заполнить его такParentслот:
// App.vue
<template>
<Parent>
<p>This content goes into the slot</p>
</Parent>
</template>
Здесь нет ничего особенного. . .
Заполнить слот подкомпонента легко, и это наиболее распространенный способ использования слота.
Однако есть ли способ контролироватьChildвнутри компонентаParentкомпонентыslotсодержание?
Другими словами: можем ли мы, чтобы дочерний компонент заполнил слот родительского компонента? Давайте посмотрим на первое решение, которое приходит мне в голову.
Используйте реквизит вниз, события вверх
Единственный способ передачи данных через дерево компонентов — использоватьprops. Способ общения заключается в использовании событий. Это означает, что если мы хотим, чтобы дочерний компонент взаимодействовал с родительским компонентом, нам нужно использовать для этого события.
Итак, мы будем использовать события для передачи контента вActionBarsв канавке
import SlotContent from './SlotContent';
export default {
name: 'Application',
created() {
// As soon as this component is created we'll emit our events
this.$emit('slot-content', SlotContent);
}
};
Упаковываем все, что хотим поместить в слот, вSlotContentв компоненте. Как только компонент приложения создан, мы выдаемslot-contentсобытие и передать компонент, который мы хотим использовать.
Наша структура компонентов выглядит следующим образом:
<template>
<div>
<FullPageError />
<ActionBar>
<Component :is="slotContent" />
</ActionBar>
<App @slot-content="component => slotContent = component" />
</div>
</template>
прослушать это событие иslotContentустановить на нашAppлюбой контент, который компонент отправляет нам. Затем используйте встроенныйComponent, компонент может отображаться динамически.
Однако передача компонентов через события кажется странной и не основной. К счастью, есть также способ полностью избежать использования событий.
использовать $опции
Поскольку компоненты Vue — это просто объекты JS, мы можем добавлять к ним любые свойства, которые захотим. Вместо того, чтобы использовать событие для передачи содержимого слота, мы можем просто добавить его как поле в компонент: //App.vue импортировать SlotContent из './SlotContent';
export default {
name: 'Application',
slotContent: SlotContent,
props: { /***/ },
computed: { /***/ },
};
Через домашнюю страницуApp.slotContentПолучить соответствующий компонент
<template>
<div>
<FullPageError />
<ActionBar>
<Component :is="slotContent" />
</ActionBar>
<App />
</div>
</template>
import App from './App';
import FullPageError from './FullPageError';
import ActionBar from './ActionBar';
export default {
name: 'Scaffold',
components: {
App,
FullPageError,
ActionBar,
}
data() {
return {
slotContent: App.slotContent,
}
},
};
Это больше похоже на статическую конфигурацию, красивее и чище, но все равно не то.
В идеале мы не смешиваем парадигмы в нашем коде, и все должно быть сделано декларативно.
Но здесь вместо того, чтобы объединять наши компоненты, мы передаем их как JS-объекты. Было бы неплохо, если бы мы могли просто написать то, что хотим, в слот обычным способом Vue.
Рассмотрим портал
Портальная технология в Vue В проекте Vue мы используем шаблоны для объявления dom.
Вложенные отношения, но иногда некоторые компоненты должны оторваться от фиксированных иерархических отношений и больше не подлежат наложению контекстов, таких как модальный и диалоговый. Такие компоненты хотят иметь возможность покинуть контекст стека, в котором находится текущий шаблон.
В Vue есть два способа добиться этого эффекта: один — использовать инструкции для работы с реальным DOM и использовать хорошо известный метод работы с DOM для добавления элемента, в котором находится инструкция. к другому узлу dom. Другой способ — определить набор компонентов, передать vnode в компоненте другому компоненту, а затем отрендерить каждый.
Они работают именно так, как вы можете себе представить. Вы можете телепортировать что угодно из одного места в другое. В нашем случае мы «телепортируем» элементы из одного места в DOM в другое.
Независимо от того, как отображается дерево компонентов, мы можем контролировать, где компоненты отображаются в DOM.
Например, предположим, что мы хотим заполнитьmodal. но нашmodalДолжен отображаться на корневой странице, чтобы мы могли правильно его переопределить. Во-первых, мы собираемсяmodalУказываем, что хотим в:
<template>
<div>
<!-- Other components -->
<Portal to="modal">
Rendered in the modal.
</Portal>
</div>
</template>
Тогда в нашемmodalВ компоненте у нас будет еще один портал, который отрисовывает контент:
<template>
<div>
<h1>Modal</h1>
<Portal from="modal" />
</div>
</template>
Это улучшение, потому что теперь мы на самом деле пишем HTML, а не просто передаем объекты. Это более декларативно и проще увидеть, что происходит в приложении.
Поскольку портал делает что-то за кулисами для рендеринга элементов в разных позициях, он полностью разрушает модель того, как работает рендеринг DOM в Vue. Похоже, вы нормально визуализируете элементы, но вообще не работаете, что может вызвать много путаницы и разочарования.
Есть еще одна большая проблема, к которой мы вернемся позже.
статус вознесения
«Поднятие состояния» означает перемещение состояния из дочернего компонента в родительский или родительский компонент, перемещая его вверх по дереву компонентов.
Это может иметь большое влияние на архитектуру приложения. Для наших целей это было бы более простым решением.
«Состояние» здесь — это то, что мы пытаемся передатьActionBarСодержимое слота компонента. Но государство содержится вPageкомпонент, мы действительно не можемpageконкретная логика перемещена вlayoutв компоненте. Наше состояние должно быть сохранено в том, что мы визуализируем динамически.Pageвнутри компонента.
Поэтому мы должны поднять весьPageКомпонент может поднять состояние. В настоящее время нашPageкомпонентLayoutПодкомпоненты компонентов:
<template>
<div>
<FullPageError />
<ActionBar />
<Page />
</div>
</template>
Чтобы поднять его, нам нужно перевернуть его и сделатьLayoutкомпоненты становятсяPageДочерний компонент компонента. нашPageКомпонент выглядит так:
<template>
<Layout>
<!-- Page-specific content -->
</Layout>
</template>
Теперь нашLayoutКомпонент будет выглядеть так, где мы можем использовать слоты для вставки содержимого страницы:
<template>
<div>
<FullPageError />
<ActionBar />
<slot />
</div>
</template>
Но это пока не позволяет нам ничего настраивать. мы должны бытьLayoutдобавить несколько именованных слотов в компонент, чтобы мы могли передатьActionBarсодержание в .
Проще всего использовать слот для полной заменыActionBarКомпоненты:
<template>
<div>
<FullPageError />
<slot name="actionbar">
<ActionBar />
</slot>
<slot />
</div>
</template>
Таким образом, если вы не укажете“actionbar”слот, используемый по умолчаниюActionBarкомпоненты. Но мы можем использовать наш собственныйActionBarКонфигурация переопределяет этот слот:
<template>
<Layout>
<template #actionbar>
<ActionBar>
<!-- Custom content that goes into the action bar -->
</ActionBar>
</template>
<!-- Page-specific content -->
</Layout>
</template>
Для меня это идеальный способ сделать это, но он требует от нас реорганизации макета страницы. Для сложных интерфейсов это может оказаться непростой задачей.
упростить это
Когда мы впервые определяем проблему:
Можем ли мы сделать так, чтобы дочерний компонент заполнил слот родительского компонента?
Но на самом деле эта проблема связана сpropsне относится. Проще говоря, речь идет о том, чтобы заставить дочерние компоненты контролировать то, что отображается за пределами их собственного поддерева.
Мы можем сформулировать задачу так
Каков наилучший способ для компонента контролировать то, что отображается за пределами его дочерних компонентов?
Изучение каждого из предложенных нами решений через эту призму дает нам новую интересную перспективу.
передать событие родительскому компоненту
Единственный способ передачи данных через дерево компонентов — использоватьprops. Способ общения заключается в использовании событий. Это означает, что если мы хотим, чтобы дочерний компонент взаимодействовал с родительским компонентом, нам нужно использовать для этого события.
статическая конфигурация
Просто предоставьте необходимую информацию другим компонентам, вместо того, чтобы активно просить другой компонент что-то сделать.
портал
Компонент не может управлять содержимым за пределами своего поддерева. Каждый метод здесь — это другой способ заставить другой компонент выполнять наши команды и управлять элементом, который нас действительно интересует.
В связи с этим используйтеportalПричина в том, что они позволяют нам инкапсулировать всю эту коммуникационную логику в отдельные компоненты.
статус вознесения
Поднятие состояния — это более простая и мощная техника, чем 3, которые мы видели ранее. Наше главное ограничение здесь заключается в том, что то, что мы хотим контролировать, находится за пределами дочерних компонентов.
Самый простой обходной путь:
Поднятие состояния и логика, управляющая этим состоянием, позволяет нам иметь более широкий диапазон компонентов и включать целевой элемент в этот компонент. Если это возможно сделать, это самый простой способ решить эту конкретную проблему и все связанные с ней проблемы.
Помните, что это не обязательно означает продвижение всей сборки. Вы также можете реорганизовать свое приложение, чтобы переместить логику в компоненты выше в дереве компонентов.
внедрение зависимости
Если кто-нибудь, знакомый с шаблонами проектирования программной инженерии, возможно, заметил, что здесь мы делаем внедрение зависимостей, технику, которую мы используем в программной инженерии на протяжении десятилетий.
Одним из его применений является написание легко настраиваемого кода. В нашем примере мы используем каждыйPageнастроены по разномуLayoutкомпоненты.
при обменеPageа такжеLayoutкомпонента, мы делаем так называемую инверсию управления.
В компонентной среде родительский компонент управляет действиями дочернего компонента, поэтому мы разрешаемPageконтролироватьLayoutкомпоненты, а неLayoutКомпонент управляет страницей.
Для этого используем слотLayoutКомпоненты предоставляют содержимое, необходимое для выполнения задачи.
Как мы видели, внедрение зависимостей может сделать наш код более модульным и простым в настройке.
Суммировать
Мы обсуждаем 4 разных подхода к этой проблеме, показывая плюсы и минусы каждого. Затем мы пошли еще дальше и превратили проблему в более общую проблему управления тем, что находится за пределами поддерева компонентов.
, состояние повышения и внедрение зависимостей — два очень полезных шаблона. Это лучшие инструменты в нашем арсенале, потому что их можно применять для решения бесчисленных проблем разработки программного обеспечения.
Но самое главное, я надеюсь, вы также можете узнать:
Превратите уродливое решение проблемы в очень элегантную, используя некоторые распространенные шаблоны программного обеспечения. Многие другие проблемы можно решить таким же образом, превратив уродливую сложную проблему в более простую и легко решаемую проблему.
Ошибки, которые могут существовать после развертывания кода, нельзя узнать в режиме реального времени.Чтобы решить эти ошибки впоследствии, много времени тратится на отладку логов.Кстати, всем рекомендую полезный инструмент мониторинга ошибок.Fundebug.
оригинал:Dev.to/Michael заменяет IE…
общаться с
Статья постоянно обновляется каждую неделю. Вы можете выполнить поиск «Big Move to the World» в WeChat, чтобы прочитать и обновить ее как можно скорее (на одну или две статьи раньше, чем в блоге). Эта статья находится на GitHub.GitHub.com/QQ449245884…Он был включен, и многие мои документы были разобраны. Добро пожаловать в Звезду и совершенство. Вы можете обратиться в тестовый центр для ознакомления во время собеседования. Кроме того, обратите внимание на паблик-аккаунт и ответьте в фоновом режиме.Благосостояние, вы можете увидеть преимущества, вы знаете.