PortalVue — Teleport для Vue шаблонов

При создании и росте сложного SPA приложения построить хорошую архитектуру становится все труднее. Порой данные, которые доступны в одном компоненте нужно отобразить в другом компоненте.

Представим себе список проблем (issues) как на github и в уголке страницы значок с числом не просмотренных проблем. Когда пользователь заходит в просмотр одной из проблем, то в списке она меняет вид, так как стала прочитанной. При этом и значок с числом должен уменьшить его. И так как это разные компоненты, которые могут быть не вложены один в другой, то встанет вопрос откуда брать данные для обои. «Vuex», — скажите вы. Но не всегда это будет удобно и верно.

PorttalVue предлагает возможность шаблон одного компонента, который использует доступные ему данные отобразить где-нибудь в другом компоненте. Или даже вовсе вне элемента, в котором выводится ваше Vue приложение.

Простой пример выглядит так:

<!-- This is inside in some component -->
<portal to="destination">
  <p>This slot content will be rendered wherever the
    <portal-target> with name 'destination'
    is located.
  </p>
</portal>

<!-- ... -->

<!-- This is somewhere in your vue application -->
<portal-target name="destination">
  <!--
  This component can be located anwhere in your App
  (i.e. right before the </body> tag, good for overlays).
  The slot content of the above portal component will be rendered here.
  -->
</portal-target>

Один компонент-донор может содержать несколько таких порталов (шаблонов для вставки в другие компоненты). Вывод можно также делать по условию через v-if.

Как было упомянуто выше такой шаблон можно отобразить где-нибудь вне разметки vue приложения. Это полезно, если ваше приложение занимает не всю страницу, а какой-нибудь ее кусочек в legacy проекте. В этом случае можно прибегнуть к следующей конструкции:

<div id="app">
  <MountingPortal mountTo="#widget" name="source" append>
    <p>Content for the Target</p>
  </MountingPortal>
<div>

<script>
  new Vue({el: '#app'})
</script>

<aside id="widget" class="widget-sidebar">
  This Element is not controlled by our Vue-App,
  but we can create a <portal-target> here with <MountingPortal>.
</aside>

Здесь используется компонент MountingPortal вместо portal, чтобы вывести содержимое в блоке с селектором #widget.

PortalVue можно также использовать вместе со  Scoped Slots. Это означает, что вы можете отправить в принимающий блок (PortalTarget) кусочек шаблона, который будет иметь возможность передать туда параметры (props):

<portal to="destination">
  <p slot-scope="{message}">{{message}}</p>
</portal>

<portal-target
  name="destination"
  :slot-props="{message: 'Hello from the Target to You!'}"
/>

Результат:

<div class="vue-portal-target">
  <p>Hello from the Target to You!</p>
</div>

В каких типичных случаях может пригодиться PortalVue?

  • Позиционирование модальных диалоговых окон и других компонентов отображаемых поверх страницы (Positioning Modals & Overlays), чтобы стили родительского элемента не влияли на дочерний и чтобы не требовалось использовать z-index
  • Если Vue приложение контролирует только часть вашей страницы, а некоторый шаблон вы хотите отобразить в другом конце страницы.

PortalVue работает на Vue 2, а для Vue 3 такая возможность есть из коробки. Называется Teleport

Полезные ссылки:

Leave a Reply

Ваш адрес email не будет опубликован. Обязательные поля помечены *