Chuyển đến nội dung chính

第 15 課:文件上傳、圖像和 SEO

文件上傳處理、S3 儲存。 @nuxt/image,響應式映像,延遲載入。 SEO 使用 useSeoMeta、useHead。網站地圖、OG 標籤、結構化資料 (JSON-LD)。

💻 程式設計 — 第 15 課 第 15 課:文件上傳、圖像和 SEO

Vue.js 和 Nuxt:從基礎到高級

第 4 部分:Nuxt 高級和全端

亞洲開發網

1. 文件上傳

// server/api/upload.post.ts
export default defineEventHandler(async (event) => {
  const files = await readMultipartFormData(event)
  if (!files || files.length === 0) {
    throw createError({ statusCode: 400, message: 'No file uploaded' })
  }

  const file = files[0]
  const allowedTypes = ['image/jpeg', 'image/png', 'image/webp']

  if (!allowedTypes.includes(file.type || '')) {
    throw createError({ statusCode: 400, message: 'Invalid file type' })
  }

  if (file.data.length > 5 * 1024 * 1024) {
    throw createError({ statusCode: 400, message: 'File too large (max 5MB)' })
  }

  const filename = `${Date.now()}-${file.filename}`

  // NuxtHub Blob (Cloudflare R2)
  const blob = await hubBlob().put(filename, file.data, {
    contentType: file.type,
  })

  return { url: blob.pathname }
})

2.@nuxt/圖片

<template>
  <!-- Basic -->
  <NuxtImg
    src="/images/hero.jpg"
    alt="Hero"
    width="800"
    height="400"
    loading="lazy"
    format="webp"
  />

  <!-- Responsive sizes -->
  <NuxtImg
    src="/images/banner.jpg"
    sizes="sm:100vw md:50vw lg:400px"
    quality="80"
    placeholder
  />

  <!-- Picture element with multiple formats -->
  <NuxtPicture
    src="/images/photo.jpg"
    format="avif,webp"
    sizes="sm:100vw md:50vw"
    quality="80"
  />
</template>

3. 使用 useSeoMeta 進行 SEO

<script setup lang="ts">
// Static SEO
useSeoMeta({
  title: 'Trang chủ | xDev',
  ogTitle: 'xDev - Nền tảng học lập trình',
  description: 'Học lập trình miễn phí với các khóa học chất lượng',
  ogDescription: 'Học lập trình miễn phí',
  ogImage: '/images/og-default.jpg',
  twitterCard: 'summary_large_image',
})
</script>
<!-- Dynamic SEO -->
<script setup lang="ts">
const { data: post } = await useFetch(`/api/posts/${slug}`)

useSeoMeta({
  title: () => post.value?.title,
  ogTitle: () => post.value?.title,
  description: () => post.value?.description,
  ogImage: () => post.value?.image,
  ogType: 'article',
  articlePublishedTime: () => post.value?.createdAt,
})

// useHead for full control
useHead({
  script: [
    {
      type: 'application/ld+json',
      textContent: JSON.stringify({
        '@context': 'https://schema.org',
        '@type': 'Article',
        headline: post.value?.title,
        image: post.value?.image,
        datePublished: post.value?.createdAt,
      }),
    },
  ],
})
</script>

4. 網站地圖生成

npx nuxi module add @nuxtjs/sitemap
// nuxt.config.ts
export default defineNuxtConfig({
  modules: ['@nuxtjs/sitemap'],
  sitemap: {
    sources: ['/api/__sitemap__/urls'],
  },
  site: {
    url: 'https://blog.xdev.asia',
  },
})

// server/api/__sitemap__/urls.ts
export default defineSitemapEventHandler(async () => {
  const posts = await db.select({ slug: schema.posts.slug, updatedAt: schema.posts.updatedAt })
    .from(schema.posts)
    .where(eq(schema.posts.published, true))

  return posts.map(post => ({
    loc: `/blog/${post.slug}`,
    lastmod: post.updatedAt,
    priority: 0.7,
  }))
})

下一篇: 實時、WebSocket 和 i18n — 即時互動與多語言。