โท Nuxt Routing
๐งช Routing
Nuxt๋ pagesํด๋๋ฅผ ๊ธฐ์ค์ผ๋ก ๋ผ์ฐํ
๋๋ค.
(src/pages/index.vue๊ฐ "/" ๋ฉ์ธ ๊ฒฝ๋ก์)
/ โ pages/index.vue/about โ pages/about/index.vue
ย
ย
๐งช Dynamic Routing
ย
pages/lesson/[title].vue
| pages/ ---| lesson/ -----| [title].vue
/* pages/lesson/[title].vue */ <script setup lang="ts"></script> <template> <div> <h2>Lesson</h2> <p> Chapter slug: <span>{{ $route.params.title }}๐ต</span> </p> </div> </template> <style scoped></style>
/* pages/index.vue */ <script lang="ts" setup> import { greeting } from '@app/ui'; const route = useRoute(); const lessonArr = [ { id: 1, title: 'vocal' }, { id: 2, title: 'piano' }, { id: 3, title: 'rock' }, { id: 4, title: 'drum' }, ]; </script> <template> <div> <p>{{ greeting }}</p> <br /> <NuxtLink v-for="lesson in lessonArr" :key="lesson.id" :to="`/lesson/${lesson.title}`" class="text-blue-700" ><p>{{ lesson.title }}</p> </NuxtLink> <p> ํ์ฌ ๊ฒฝ๋ก: <code>{{ route.path }}</code> </p> </div> </template> <style lang="scss" scoped></style>
ย
โ ์ฐธ๊ณ ) ๋งค๊ฐ๋ณ์๋ฅผ ์ ํ์ฌํญ์ผ๋ก ์ง์ ํ๋ ค๋ฉด ๋งค๊ฐ๋ณ์๋ฅผ ์ด์ค ๋๊ดํธ๋ก ๋ฌถ์ด์ผ ํ๋ค.
pages/lesson/[[title]].vuepages/[[slug]]/index.vue
๋ pages/[[slug]].vue
์ ๊ฐ๋ค.ย
ย
๐งช Catch-all Route
ย
ํน์ ๊ฒฝ๋ก ํ์์ ๋ชจ๋ ๊ฒฝ๋ก๋ฅผ ์ฒ๋ฆฌํ ์ ์๋ ๋ผ์ฐํ
๊ธฐ๋ฒ์ด๋ค.
์ด๋ค ๊ฒฝ๋ก๋ ํด๋น ๋ผ์ฐํธ์์ ์ฒ๋ฆฌํ ์ ์๋ค.
[...slug].vue
์ ๊ฐ์ ํ์ด์ง ์ปดํฌ๋ํธ ์ด๋ฆ์ ์ฌ์ฉํ์ฌ ๊ฒฝ๋ก๋ฅผ ๋ง๋ค๋ฉด ๋๋ค.ย
pages/[...slug].vue
<template> <p>{{ $route.params.slug }}</p> </template>
ย
/bt21/tata๋ก ์ด๋ํ๋ฉด ์๋์ ๊ฐ์ด ๋ณด์ฌ์ง๋ค.
<p>["bt21", "tata"]</p>
ย
ย
๐งช NuxtLink
๊ฒฝ๋ก ์ด๋์ด ๊ฐ๋ฅํ๋ค.
<NuxtLink to="/profile">my profile</NuxtLink>
ย
ย
๐งช useRouter
ย
๊ฒฝ๋ก ์ด๋
<script setup> const router = useRouter() </script>
ย
back()
: ๋ค๋ก๊ฐ๊ธฐ router.go(-1)์ ๋์ผํจ.
forward()
: ์์ผ๋ก๊ฐ๊ธฐ router.go(1)์ ๋์ผํจ.
push()
: ์ URL๋ก ์ด๋
replace()
: ํ์ฌ ๊ฒฝ๋ก ์ URL๋ก ์๋ก ๊ณ ์นจconst router = useRouter() router.addRoute({ name: 'home', path: '/home', component: Home }) router.removeRoute('home') router.getRoutes() router.hasRoute('home') router.resolve({ name: 'home' })
ย
ํ
ํ๋ฆฟ ๋ด์ ๋ผ์ฐํฐ ์ธ์คํด์ค๋ง ํ์ํ ๊ฒฝ์ฐ
<template> <button @click="$router.back()">Back</button> </template>
ย
ย
๐งช NavigateTo
ย
๊ฒฝ๋ก ์ด๋
<script setup lang="ts"> await navigateTo('/search') await navigateTo({ path: '/search' }) await navigateTo({ path: '/search', query: { page: 1, sort: 'asc' } }) </script>
ย
ย
๐งช useRoute
ย
ํ์ฌ ๊ฒฝ๋ก๋ฅผ ๋ฐํ
<script setup lang="ts"> const route = useRoute() </script> <template> <p>ํ์ฌ ๊ฒฝ๋ก: {{ route.path }}</p> </template>
ย
fullPath
: ๊ฒฝ๋ก, ์ฟผ๋ฆฌ ๋ฐ ํด์๋ฅผ ํฌํจํ๋ ํ์ฌ ๊ฒฝ๋ก์ ์ฐ๊ฒฐ๋ ์ธ์ฝ๋ฉ๋ URL
hash
: #์ผ๋ก ์์ํ๋ URL์ ๋์ฝ๋ฉ๋ ํด์ ์น์
matched
: ํ์ฌ ๊ฒฝ๋ก ์์น์ ์ผ์นํ๋ ์ ๊ทํ๋ ๊ฒฝ๋ก ๋ฐฐ์ด
meta
: ๋ ์ฝ๋์ ์ฒจ๋ถ๋ ์ฌ์ฉ์ ์ ์ ๋ฐ์ดํฐ
name
: ๊ฒฝ๋ก ๋ ์ฝ๋์ ๊ณ ์ ์ด๋ฆ
path
: URL์ ์ธ์ฝ๋ฉ๋ ๊ฒฝ๋ก ์ด๋ฆ ์น์
redirectedFrom
: ํ์ฌ ๊ฒฝ๋ก ์์น์ ๋๋ฌํ๊ธฐ ์ ์ ์ ๊ทผ์ ์๋ํ ๊ฒฝ๋ก ์์นย
ํ
ํ๋ฆฟ ๋ด์ ๋ผ์ฐํธ ์ธ์คํด์ค๋ง ํ์ํ ๊ฒฝ์ฐ
<template> <p>ํ์ฌ ๊ฒฝ๋ก: {{ $route.path }}</p> </template>
ย
ย
โท Nuxt layouts
๐งช layouts
ย
src/layouts/default.vue
/* src/layouts/default.vue */ <script lang="ts" setup></script> <template> <div class="h-[50px] flex items-center justify-center w-full fixed bg-black"> <p class="text-cyan-500">~๊ธฐ๋ณธ ๋ ์ด์์~</p> </div> <div class="h-screen flex-col flex justify-center items-center"> <slot /> // โญ๏ธ ๊ผญ ์ถ๊ฐ </div> </template> <style scoped></style>
ย
์ปค์คํ
layout
-| layouts/ ---| default.vue ---| custom.vue
/* layouts/custom.vue */ <script setup lang="ts"> import Header from '@/components/Header/Header.vue'; </script> <template> <div> // ํค๋ => Header์ปดํฌ๋ํธ <Header /> <slot></slot> </div> </template>
/* pages/about/index.vue */ <script setup lang="ts"> definePageMeta({ layout: 'custom' }) </script>
ย
ย
โท SEO and Meta
ย
๋ชจ๋ฐ์ผ์์ ์น์ฌ์ดํธ ํ๋ฉด ํ๋ ์ ๋๊ฒ ํ๋ ๋ฐฉ๋ฒ
export default defineNuxtConfig({ app: { head: { charset: 'utf-8', viewport: 'width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no', }, },
ย
ย
โท useSeoMeta
<script setup lang="ts"> useSeoMeta({ title: 'My Amazing Site', ogTitle: 'My Amazing Site', description: 'This is my amazing site, let me tell you all about it.', ogDescription: 'This is my amazing site, let me tell you all about it.', ogImage: '<https://example.com/image.png>', twitterCard: 'summary_large_image', }) </script>
ย
๋ฐ์ํ ํ๊ทธ์ผ ๊ฒฝ์ฐ
<script setup lang="ts"> const title = ref('My title') useSeoMeta({ title, description: () => `description: ${title.value}` }) </script>
ย
ย
โท Data fetching
๐งช useFetch
ย
useAsyncData๋ณด๋ค ์ฌ์ฉ๋ฒ์ด ๊ฐ๋จํ๋ค.
const { data: product, pending, error } = await useFetch(() => `https://dummyjson.com/products/${id.value}`)
๐งช useAsyncData
ย
const { data: product, pending, error } = await useAsyncData(() => { return $fetch(`https://dummyjson.com/products/${id.value}`) }, { watch: [id] })
ย
ย
๐งช Data fetching ์์ ์ฝ๋
ย
<script setup> const id = ref(1) const { data: product, pending, error } = await useFetch(() => `https://dummyjson.com/products/${id.value}`) /* Same as: const { data: product, pending, error } = await useAsyncData(() => { return $fetch(`https://dummyjson.com/products/${id.value}`) }, { watch: [id] }) */ </script> <template> <div> <p>Result of <code><https://dummyjson.com/products/></code><input type="number" v-model="id" /></p> <p><button @click="id--">Previous</button> - <button @click="id++">Next</button></p> <p v-if="pending">Fetching...</p> <pre v-else-if="error">{{ error }}</pre> <pre v-else>{{ product }}</pre> <NuxtLink to="/">Back home</NuxtLink> </div> </template>
ย
ย
๐งช Static Hosting - SSG
ย
defineNuxtConfig({ ssr: false, nitro: { prerender: { routes: ['/', '/e-book'], // SSG ์ ์ฉํ ํ์ด์ง ๊ฒฝ๋ก ์ ๋ ๊ณณ } } })
ย
ย
๐งช ClientOnly
ย
์ปดํฌ๋ํธ๋ฅผ ํด๋ผ์ด์ธํธ ์ธก์์๋ง ๋ ๋๋งํ๋ค.
<ClientOnly> <Comment /> </ClientOnly>
ย
๋๋ ํ์ผ ์ด๋ฆ์
.client
๋ฅผ ๋ถ์ฌ ํด๋ผ์ด์ธํธ ์ธก์์๋ง ๋ ๋๋ง์ด ๋๋๋ก ํ ์ ์๋ค.| components/ --| Comments.client.vue
/* Comments.client.vue */ <template> <div> // this component will only be rendered on client side <Comments /> </div> </template>
ย
โ๏ธ์ฃผ์) .client๊ตฌ์ฑ ์์๋ ๋ง์ดํธ๋ ํ์๋ง ๋ ๋๋ง ๋๋ค. ๋ ๋๋ง๋ ํ
ํ๋ฆฟ์ ์ฌ์ฉํ๋ ค๋ฉด onMounted()ํ
์ .await nextTick()์ ์ถ๊ฐํ๋ฉด ๋จ.
ย
ย
ย
ย