[Vue.js] Portal-Vue 라이브러리 사용하기

하수도키

·

2021. 3. 26. 17:58

728x90
반응형
SMALL
Portal은 부모 컴포넌트의 DOM 계층 구조 바깥에 있는 DOM 노드로 자식을 렌더링 하는 최고의 방법을 제공합니다.

위에 문구는 리액트 홈페이지에서 나온 Portals에 대한 설명이다.

Vue2에서는 현재 제공하지 않아 Portal-Vue 라는 라이브러리를 사용한다.

https://portal-vue.linusb.org/guide/getting-started.html#what-is-portalvue

Vue3에서는 Portal(Teleport) 을 지원한다!!

hasudoki.tistory.com/entry/VueJs-3-Composition-API-%EC%82%B4%ED%8E%B4%EB%B3%B4%EA%B8%B0-5Teleport?category=1064875

 

Vue.Js 3 Composition API 살펴보기 - 5(Teleport)

Vue3 Composition API에 대해 알아보자 Vue3가 나오면서 Composition API가 제공되고 있다. Composition API가 왜 나왔는지 알아보고 뭔지 알아보자. 개요 Teleport Teleport Vue 컴포넌트 설계는 비지니스 로직..

hasudoki.tistory.com

PortalVue는 2개의 컴포넌트(요소)가 필요하다

렌더링 될 컴포넌트와 이 컴포넌트가 보이는 위치에 있는 컴포넌트

말을 어렵지만, 즉 내용 컴포넌트, 위치 컴포넌트라고 이해하면 될 것 같다.

설치

npm install --save portal-vue

# or with yarn
yarn add portal-vue
// main.js
import PortalVue from 'portal-vue'

Vue.use(PortalVue)
<portal to="destination">
  <p>This slot content will be rendered wherever the
    <portal-target> with name 'destination'
    is located.
  </p>
</portal>

<portal-target name="destination">
  <!--
  This component can be located anwhere in your App.
  The slot content of the above portal component will be rendered here.
  -->
</portal-target>
  • portal 이 렌더링 될 컴포넌트(요소)
  • portal-target이 보일 위치에 있는 컴포넌트(요소)
  • to, name 으로 매칭 한다.

기본 예제

App.vue 안에 Render.vue 컴포넌트가 있는데 Render컴포넌트에서 portal을 생성하고 App.vue에 portal-target을 생성한다.

위 내용은 Render 컴포넌트에서 App 컴포넌트 DOM을 조작한다고 이해하면 된다.

// App.vue
<template>
  <div id="app">
    <Render />
    <div id="nav">
      <router-link to="/">Home</router-link> |
      <router-link to="/about">About</router-link>
    </div>
    <router-view />
    <portal-target name="destination"></portal-target>
  </div>
</template>

<script>
import Render from "@/components/Render.vue";
export default {
  components: {
    Render,
  },
  data() {
    return {};
  },
};
</script>

// Render.vue
<template>
  <div>
    <div>Render</div>
    <portal to="destination">
      <p>Render 컴포넌트에서 portal이 그려주고 있다.</p>
    </portal>
  </div>
</template>

<script>
export default {};
</script>

Portal-Vue 예제

위 이미지와 같이 제목은 Render 컴포넌트고 하단 portal-target이 제대로 나오는 걸 볼 수 있다.

기능

v-if 사용하기

// Render.vue
<template>
  <div>
    <div>Render</div>
    <portal to="destination" v-if="usePortal">
      <p>test this is portal in About page</p>
    </portal>
  </div>
</template>

usePortal 값에 따라 portal-target에서 show/hide가 가능하다.

1개의 Target에 여러 개의 Portal 사용하기

// Render.vue
<template>
  <div>
    <div>Render</div>
    <portal to="destination" :order="1">
      <p>test this is portal page 1</p>
    </portal>
    <portal to="destination" :order="2">
      <p>test this is portal page 2</p>
    </portal>
  </div>
</template>

// result
<div class="vue-portal-target">
  <p>test this is portal page 1</p>
  <p>test this is portal page 2</p>
</div>

Vue 앱 밖 영역에 렌더링 하기

지금까지 예제들은 모두 App.vue파일 <div id="app"> 안에서 렌더링 했다.

이번 예제는 <div id="app"> 밖에서 렌더링 하는 방법을 알아보자.

을 사용하면 된다.

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

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

위와 같이 예제 코드를 작성하면 된다.

append 속성을 사용하면 aside 태그 뒤에 추가된다.

기능은 이 정도로 설명하고 자세한 내용은 아래 링크를 참조하자.

https://portal-vue.linusb.org/guide/getting-started.html

실전 예제

간단하게 Modal 컴포넌트를 index.html 만든 영역에 띄우는 코드이다.

// public/index.html
<body>
    <noscript>
      <strong
        >We're sorry but codesandbox doesn't work properly without JavaScript
        enabled. Please enable it to continue.</strong
      >
    </noscript>
    <div id="app"></div>
    <div id="modal-target"></div>
    <!-- built files will be auto injected -->
  </body>
// App.vue
<template>
  <div id="app">
    <Modal />
  </div>
</template>

<script>
import Modal from "@/components/Modal";
export default {
  name: "App",
  components: {
    Modal,
  },
};
</script>
// Modal.vue
<template>
  <div>
    <button @click="isShowModal = true">Modal Target</button>
    <MountingPortal mountTo="#modal-target">
      <div class="modal" v-if="isShowModal">
        <div class="modal-overlay"></div>
        <div class="modal-content">
          <p>모달입니다.</p>
          <button class="modal-close-btn" @click="isShowModal = false">
            닫기
          </button>
        </div>
      </div>
    </MountingPortal>
  </div>
</template>

<script>
export default {
  data() {
    return {
      isShowModal: false,
    };
  },
};
</script>

body 영역 마지막에 클래스명인 vue-portal-target div영역이 추가되었다.

Portal-Vue 예제 2

전체 코드는 아래서 확인 가능하다.

https://codesandbox.io/s/objective-hooks-zk6no

 

objective-hooks-zk6no - CodeSandbox

objective-hooks-zk6no by shldhee using @vue/cli-plugin-babel, portal-vue, vue

codesandbox.io

결론

포스팅 작성하고 검색하다가 좋은 포스팅을 발견했는데 이걸 바탕으로 쓸걸 후회 중이다.

아래 링크를 통해 더 자세하고 좋은 포스팅을 참고하면 좋을 것 같다.

https://blog.logrocket.com/dynamic-component-rendering-with-vue-portal/

728x90
반응형
LIST