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

TATAยท2023๋…„ 10์›” 27์ผ
1

Nuxt

๋ชฉ๋ก ๋ณด๊ธฐ
1/1

โ–ท 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]].vue


๊ฒฝ๋กœ ์ด๋™์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

<NuxtLink to="/profile">my profile</NuxtLink>

๐Ÿงช useRouter

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

NavigateTo

๊ฒฝ๋กœ ์ด๋™

<script setup lang="ts">
await navigateTo('/search')

await navigateTo({ path: '/search' })

await navigateTo({
  path: '/search',
  query: {
    page: 1,
    sort: 'asc'
  }
})
</script>

๐Ÿงช useRoute

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

layouts
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

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

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

Data fetching ์˜ˆ์ œ

๐Ÿงช useFetch

useFetch

useAsyncData๋ณด๋‹ค ์‚ฌ์šฉ๋ฒ•์ด ๊ฐ„๋‹จํ•˜๋‹ค.

const { data: product, pending, error } = await useFetch(() => `https://dummyjson.com/products/${id.value}`)

๐Ÿงช useAsyncData

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

Static Hosting

defineNuxtConfig({
  ssr: false,
  nitro: {
    prerender: {
      routes: ['/', '/e-book'], // SSG ์ ์šฉํ•  ํŽ˜์ด์ง€ ๊ฒฝ๋กœ ์ ๋Š” ๊ณณ
    }
  }
})

๐Ÿงช ClientOnly

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



๐Ÿ‘‰ Nuxt ์˜ˆ์ œ(๊ณต์‹๋ฌธ์„œ)

profile
๐Ÿพ

0๊ฐœ์˜ ๋Œ“๊ธ€