banner

Vue3 Teleport, Pinia랑 찰떡궁합?!

#vue#vue3#teleport#pinia#js

2023.12.27

🙋🏻‍♂️: wrapper 하위로 레이어 형태로 된 다양한 알럿이 떴으면 좋겠어요.
👨🏻‍💻: 매 컴포넌트마다 불러와서 해야하나..?

Vue에는 어디서 붙여도 원하는 곳으로 보낼 수 있는 Teleport가 있습니다!!

🌟 요약

  1. 텔레포트 레이어 컴포넌트를 만든다.
  2. 레이어 내 데이터들은 pinia로 관리한다.
  3. 최상위 컴포넌트에 레이어 컴포넌트를 등록해놓는다.
  4. 필요한 곳에서 pinia를 통해 호출한다.

😋 Vue Teleport 만들기

먼저 공통으로 쓰일 레이어를 만들어 줍니다.

<div id="popWrapper" class="toast-popup">
  <div id="popContainer" class="popup-box" role="dialog">
    <h2 class="title-popup">{{ title }}</h2>
    <p class="text-popup" v-html="content" />
    <div class="btn-area02">
      <div v-if="btnType === 2" class="btn-unit04">
        <a @click="onCancel?.(), store.closeLayer?.()">
          <strong>{{ cancel }}</strong>
        </a>
      </div>
      <div class="btn-unit01">
        <a @click="onConfirm?.(), store.closeLayer?.()">
          <strong>{{ confirm }}</strong>
        </a>
      </div>
    </div>
  </div>
</div>

위 마크업에 쓰인 함수 및 변수는 pinia를 통해 컨트롤하는 값들이 될 것입니다.

이제 위 마크업을 <Teleport>컴포넌트로 감싸줍니다.

<Teleport v-if="isOpen" to="#container"> ... </Teleport>

to 속성에 어느 element에 붙을지 지정을 해줍니다.

<script setup>
import { useLayerStore } from '@/stores/useLayerStore';
import { storeToRefs } from 'pinia';

const store = useLayerStore();

const {
  isOpen,
  title,
  content,
  btnType,
  confirm,
  cancel,
  onConfirm,
  onCancel,
} = storeToRefs(store);
</script>

storeToRefs는 반응성을 살려주는 역할을 합니다.

🐣 Pinia로 상태관리 만들기

stores/useLayerStore.js파일을 만들고 다음과 같이 작성해줍니다.

import { defineStore } from "pinia";
import { computed, ref } from "vue";
export const useLayerStore = defineStore("layer", () => {
  const state = ref({
    isOpen: false,
    title: "",
    content: "",
    confirm: "확인",
    cancel: "",
    btnType: 1,
    onConfirm: null,
    onCancel: null,
    typeClass: "toast-popup",
  });

  // getter
  const isOpen = computed(() => state.value.isOpen);
  const title = computed(() => state.value.title);
  const content = computed(() => state.value.content);
  const btnType = computed(() => state.value.btnType);
  const confirm = computed(() => state.value.confirm);
  const cancel = computed(() => state.value.cancel);
  const onConfirm = computed(() => state.value.onConfirm);
  const onCancel = computed(() => state.value.onCancel);
  const typeClass = computed(() => state.value.typeClass);

  // action
  function openLayer(data) {
    state.value.isOpen = !state.value.isOpen;
    state.value.title = data.title;
    state.value.content = data.content;
    state.value.confirm = data.confirm ?? state.value.confirm;
    state.value.cancel = data.cancel ?? state.value.cancel;
    state.value.btnType = data.btnType || 1;
    state.value.onConfirm = data.onConfirm;
    state.value.onCancel = data.onCancel;
    state.value.typeClass = data.typeClass;
  }
  function closeLayer() {
    state.value.isOpen = false;
  }

  return {
    isOpen,
    title,
    content,
    btnType,
    confirm,
    cancel,
    onConfirm,
    onCancel,
    typeClass,
    openLayer,
    closeLayer,
  };
});

이제 어느 컴포넌트에서든 openLayer함수를 호출하여 레이어를 띄울 수 있습니다!

import { useLayerStore } from "@/stores/useLayerStore";

const { openLayer } = useLayerStore();

openLayer({
  title: "안내",
  content: `내용`,
  btnType: 2,
  confirm: "회원가입",
  onConfirm: () => {
    router.push("/signup");
  },
  cancel: "취소",
});

끝까지 읽어주셔서 감사합니다.

0초 동안 운영중...

Copyright © 2023, All right reserved.