[Nuxt] ๐ŸงชNuxt ์‚ฌ์šฉ๋ฒ• (v3)

โ–ท 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()์„ ์ถ”๊ฐ€ํ•˜๋ฉด ๋จ.
ย 
ย 
ย 
ย 

MORE POSTS

you might like

MORE POSTS

you might like

MORE POSTS

you might like

MORE POSTS

you might like