[Vue.js] Portal-Vue 라이브러리 사용하기
하수도키
·2021. 3. 26. 17:58
Portal은 부모 컴포넌트의 DOM 계층 구조 바깥에 있는 DOM 노드로 자식을 렌더링 하는 최고의 방법을 제공합니다.
위에 문구는 리액트 홈페이지에서 나온 Portals에 대한 설명이다.
Vue2에서는 현재 제공하지 않아 Portal-Vue 라는 라이브러리를 사용한다.
https://portal-vue.linusb.org/guide/getting-started.html#what-is-portalvue
Vue3에서는 Portal(Teleport) 을 지원한다!!
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>
위 이미지와 같이 제목은 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영역이 추가되었다.
전체 코드는 아래서 확인 가능하다.
https://codesandbox.io/s/objective-hooks-zk6no
결론
포스팅 작성하고 검색하다가 좋은 포스팅을 발견했는데 이걸 바탕으로 쓸걸 후회 중이다.
아래 링크를 통해 더 자세하고 좋은 포스팅을 참고하면 좋을 것 같다.
https://blog.logrocket.com/dynamic-component-rendering-with-vue-portal/