요즘 정신이 없다.. 블로그 글 안 쓴 지도 거의 한 달이 다 되어 가는지라
저번에 생각해두었던 keep-alive + 탭을 구현해보려 한다
들어가기 전에 현재 하고 있는 프로젝트 링크를 남긴다
설명 다 필요 없고 코드만 원하면 프로젝트 가서 코드를 보면 된다
사전준비
일단 vue devtools부터 설치해 주자
https://chrome.google.com/webstore/detail/vuejs-devtools/nhdogjmejiglipccpnnnanhbledajbpd
여태 설명하면서 느끼는 것이지만 백번 글로 보는 것보다
한 번 그냥 보는 것이 제일 이해가 쉽다
두 번째로는 이전 글을 읽고 오자
대략적 방법 설명
이전 글을 보았다면 include는 string array를 값으로 가진 다는 것을 알 수 있을 것이다
그리고 vue 특성상 반응형 함수로 선언해 둔 ex. (computed, reactive, ref 등) 변수들은
변경이 되면 재 렌더링 되는 것도 알고 있을.. 것이라고 믿는다
그럼 우리가 탭을 구현하기 위해 할 것은 이하의 스탭과 같다
1. 페이지 이동 시 어디로 이동했는지 파악
2. 페이지 이동 시에 이미 이동한 페이지인지 파악
3. 페이지 이동 기록을 동적으로 로드할 수 있는 기능 구현
4. 페이지 이동 기록을 삭제할 수 있는 기능 구현
말로 길게 늘어뜨려서 그렇지 그냥 탭! 하면 이거! 하는 기분으로 바로 나와야 한다
먼저 이 기능들을 사용하기 위해서 필수적인 것들을 설명해 보자
vue-router / Navigation guards
vue-router에서는 해당 라우터 이동시, 로직을 방어해주는 함수가 존재한다
이번 탭 구현에 필수적인 녀석이라 볼 수 있다
일단 코드를 보면서 이해해 보자
import type { App } from 'vue'
import { createRouter, createWebHistory } from 'vue-router'
import { getEnv } from '../module/env'
import { defaultRoute } from './routes/default'
import { menuBuilder } from './tools/menuBuilder'
import { routerGuard } from './tools/routerGuard'
const router = createRouter({
history: createWebHistory(getEnv('VITE_PUBLIC_PATH')),
routes: [...defaultRoute],
scrollBehavior: () => ({ left: 0, top: 0, behavior: 'smooth' }),
})
const initRouter = (app: App<Element>) => {
menuBuilder(router)
router.afterEach((to, _from, _failure) => {
to.meta.cache && useTabStoreWithoutInit().addCache(to.name as string)
})
app.use(router)
}
export { initRouter, router }
다른 곳은 다 볼 필요 없고 router.afterEach만 보자
허무할 정도로 짧은 코드이지 않은가?
네비게이션 가드에서 성공적으로 페이지에 랜딩 할 경우
1. 해당 라우터 요소의 cache를 파악
(이건 따로 라우트 빌딩 할 때 설정해 주는 값이다, 캐시 설정을 하지 않는다면 없어도 무방하다)
2. 해당 라우터 요소의 name값을 가지고 tab정보를 활용할 스토어에 저장
빠르게 스토어 코드도 확인하자
tab store
import { defineStore } from 'pinia'
import { computed, reactive } from 'vue'
import { router } from '../../router/router'
import { store } from '../store'
const useTabStore = defineStore('tab', () => {
const state = reactive({
cachedTab: [] as string[],
})
const addCache = (menuname: string) => (state.cachedTab = [...new Set([...state.cachedTab, menuname])])
const getCacheList = () => computed(() => state.cachedTab).value.filter((val) => val !== 'Home')
const removeCache = (menuname: string) => {
const leftTab = state.cachedTab[state.cachedTab.indexOf(menuname) - 1]
const rightTab = state.cachedTab[state.cachedTab.indexOf(menuname) + 1]
state.cachedTab = state.cachedTab.filter((item) => item !== menuname)
if (router.currentRoute.value.name === menuname) {
router.push({ name: leftTab || rightTab || 'Home' })
}
}
const refreshCurrentTab = () => {
const menuname = router.currentRoute.value.name as string
const orgTabLocation = state.cachedTab.indexOf(menuname)
const currentRouteName = router.currentRoute.value.name as string
state.cachedTab.splice(orgTabLocation, 1)
router.push({ name: 'Refresh' }).then(() => {
state.cachedTab.splice(orgTabLocation, 0, menuname)
state.cachedTab = state.cachedTab.filter((val) => val !== 'Refresh')
router.push({ name: currentRouteName })
})
}
return {
state,
addCache,
getCacheList,
removeCache,
refreshCurrentTab,
}
})
export { useTabStore }
위에서 나온 addCache는 해당 스토어의 cachedTab에 저장,
각각 READ/DELETE에 해당하는 getCacheList와 removeCache를 작성한다
결국 판단하는 값은 name이므로 웬만하면 그냥 name값이 파라미터가 된다
refresh 또한 구현이 가능하다 단지 캐시 값을 날린다 생각으로 함수를 작성한다
코드가 그리 어려운 것은 아니기에 따로 설명은 하지 않는다
내비게이션가드와 tab스토어는 이제 router이동 시마다 이동 정보를 캐싱하는 상태가 된다
여기서, 다시 keep-alive가 있는 코드로 가보자
keep-alive
<template>
<RouterView v-slot="{ Component, route }">
<Transition name="scale" mode="out-in">
<KeepAlive :include="cachedView">
<component :is="Component" :key="route.fullPath" />
</KeepAlive>
</Transition>
</RouterView>
</template>
<script setup lang="ts">
import { computed } from 'vue'
import { useTabStoreWithoutInit } from '../store/modules/tab'
const cachedView = computed(() => useTabStoreWithoutInit().getCacheList())
</script>
<style>
처음에 설명했듯, 반응형 함수로 선언해 둔 변수를 가지는 컴포넌트는 변수 값이 바뀌면 자동으로 다시 랜더링 된다
이것을 이용하여 우리는 아까 작업해 둔 getCacheList값을 가지고 keep-alive값이 가질 include값을 동적으로 설정한다
자, 이것을 확인하기 위해서는 이제 navibar에 탭 랜더링하는 컴포넌트도 만들고 붙여주고..
( 이건 그냥 store값 기준 동적 렌더라 따로 코드는 포함하지 않는다 )
( 예시에 나온 코드는 제일 하단에 지금 작성 중인 프로젝트 코드에 가서 보면 된다)
붙여둔 tab 컴포넌트로 keep-alive를 확인해 보자
현재 tab컴포넌트들은 getCacheList값들을 동적으로 렌더링 한다
또한 클릭하면 해당 페이지로, x를 누르면 removeCache함수로 캐시를 삭제한다
keep-alive 또한 getCacheList값을 기준으로 include값을 포함한다
이 상태에서는 Menu 페이지와 Locale페이지는 캐시가 된 상태이므로
두 페이지에서 어떤 작업을 하던 캐시가 남아있기에 다시 그 페이지로 돌아오면 작업하던 값이 남는다
여기서 내가 언어 메뉴를 삭제한다면??
사진과 같이 캐시 된 것이 바로 사라지는 것을 확인할 수 있다
실제로 메뉴 페이지 가보면 내가 작업하던 값들이 다 사라져 있는 것이 확인가능하다
하나하나 단계를 밟아가며 설명한다고 늦었지만 결국 요약하면 아래와 같다
1. tab store에 캐시리스트를 등록
2. router/navigater guard에서 페이지 이동시 tab store에 캐시리스트를 등록 및 관리
3. keep-alive와 tab컴포넌트에서 tab store의 캐시리스트 값을 바라보게끔 컴포넌트들을 작성
4. 주체인 tab store에서 해당 캐시리스트를 조작하여 캐시리스트를 바라보는 컴포넌트들을 리랜더링
이게 끝이다
이렇게 설명은 해두었지만.. 요약한 것을 보고 자신의 손으로도 구현해 보자
진짜 읽고 복붙 해서 쓰는 것과 읽고 다시 구현해 보는 것은 천지 차이가 있다 생각하기에..
그래도 코드만 원해!!라는 분은 필자가 작성 중인 아래의 레포지토리로 달려가보자
'프로그래밍 > B_CMS' 카테고리의 다른 글
[B_CMS] vue의 keep-alive를 사용해보자 feat. vue-router (0) | 2023.10.14 |
---|