使用 Next.js、React、MDX、Tailwind CSS、Shadcn/UI 和 TypeScript 打造現代化一頁式電商網站:完整開發指南

使用 Next.js、React、MDX、Tailwind CSS、Shadcn/UI 和 TypeScript 打造現代化一頁式電商網站:完整開發指南
Ian Chou
Ian Chou

深入探討如何使用 Next.js 14/15、React 19、MDX、Tailwind CSS、Shadcn/UI 和 TypeScript 這套現代化技術堆棧,從零開始打造一個功能完整的一頁式電商網站。包含詳細實作步驟、最佳實踐和進階功能。

Next.jsReactMDXTailwind CSSShadcn/UITypeScript電商一頁式網站

前言

在 2025 年的網頁開發領域中,打造一個高效能、美觀且功能完整的電商網站已經變得比以往更加重要。但是,什麼才是真正的 One Page Store?透過研究 Whirly Birdie 字體展示和 USB Club Transport 這樣的經典案例,我們發現真正的 One Page Store 不僅僅是技術實作,更是一種全新的電商藝術表達形式。

本文將深入探討如何使用 Next.js 14/15、React 19、MDX、Tailwind CSS、Shadcn/UI 和 TypeScript 這套現代化技術堆棧,從零開始打造一個真正意義上的沉浸式 One Page Store。

🎨 重新定義 One Page Store

傳統電商 vs. 真正的 One Page Store

傳統電商頁面的特點:

  • 多產品展示目錄
  • 功能性導向設計
  • 標準化佈局模版
  • 理性購買決策流程

真正的 One Page Store 特色:

  • 單一產品聚焦 ✨ - 專注一個主打產品的完整故事
  • 沉浸式體驗設計 🌟 - 創造情感連結和品牌認同
  • 藝術化創意設計 🎪 - 獨特視覺效果和互動元素
  • 完整情感旅程 📚 - 從認知到渴望到購買的流暢體驗

核心設計理念

真正的 One Page Store 就像是一個數位產品博物館,每個元素都是為了一個目標:讓訪客完全沉浸在產品的世界中,從理性認知轉化為情感渴望,最終完成購買行為。

🎯 One Page Store 的六大核心特色

1. 🎨 單一產品聚焦

真正的 One Page Store 不是產品目錄,而是一個產品的完整宇宙

  • 深度展示:從外觀、功能到使用場景的 360 度呈現
  • 品牌塑造:創造產品的獨特身份和情感價值
  • 故事敘述:透過設計語言訴說產品背後的故事

2. 🌟 沉浸式體驗設計

創造讓使用者「忘記時間」的體驗:

  • 震撼的 Hero 區塊:全屏背景、漸變效果、動態文字
  • 情感化文案:「重新定義你的聆聽體驗」而非「高品質耳機」
  • 視覺衝擊力:大膽的色彩搭配和版式設計

3. 🎪 互動體驗元素

讓使用者「參與」而非僅僅「瀏覽」:

  • 即時體驗:點擊按鈕切換產品效果展示
  • 動畫回饋:滾動視差、淡入淡出、脈衝動畫
  • 感官刺激:聲音、觸覺回饋(震動)等多感官體驗

4. 📚 完整銷售故事

從注意力到購買的情感旅程:

  1. 吸引注意 → 震撼的視覺和獨特價值主張
  2. 建立需求 → 展示痛點和解決方案
  3. 互動體驗 → 讓用戶「感受」產品價值
  4. 社會證明 → 真實用戶評價和成功案例
  5. 促成購買 → 緊迫感和無風險保證

5. ✨ 獨特的設計元素

區別於普通網頁的藝術化處理:

  • 創意圖標系統:🔇🔋🎵⚡ 讓功能更生動
  • 漸變文字效果:重點詞彙使用彩色漸變突出
  • 流暢滾動體驗:各區塊間的平滑過渡
  • 響應式互動:根據設備特性優化體驗

6. 🛒 無縫購買流程

消除所有購買摩擦:

  • 一鍵購買:直接加入購物車並開啟結帳
  • 信任標識:免費送貨、退款保證、保固承諾
  • 緊迫感設計:限時特價、庫存提醒、節省金額顯示

技術堆棧深度解析

1. Next.js 15 - 強大的 React 框架

Next.js 是由 Vercel 開發的全端 React 框架,提供了檔案式路由、動態路由和條件渲染等核心功能。最新版本的 Next.js 15 帶來了許多重要的改進:

  • App Router:提供更靈活的路由系統和佈局管理
  • Server Components:預設使用伺服器元件,提升效能和 SEO
  • Turbopack:更快的開發環境建置速度
  • 內建圖片優化:自動優化圖片載入和顯示

2. React 19 - 現代化的 UI 函式庫

React 19 作為最新版本,帶來了許多效能優化和開發體驗的改進:

  • 並行渲染:提升大型應用的渲染效能
  • 自動批次更新:減少不必要的重新渲染
  • 改進的錯誤邊界:更好的錯誤處理機制

3. MDX - 強大的內容管理解決方案

MDX 是 Markdown 的擴展,允許你在 Markdown 檔案中直接撰寫 JSX。這對於電商網站來說特別有用,因為:

  • 豐富的產品描述:可以在產品描述中嵌入互動式元件
  • 動態內容:輕鬆整合圖表、動畫和其他 React 元件
  • 易於維護:內容與程式碼分離,便於非技術人員編輯

4. Tailwind CSS - 實用優先的 CSS 框架

Tailwind CSS 提供了一套完整的實用類別系統,讓你能夠快速建立美觀的介面:

  • 快速開發:直接在 HTML 中使用預定義的類別
  • 一致性設計:確保整個網站的視覺一致性
  • 高度可自訂:可以根據品牌需求調整設計系統

5. Shadcn/UI - 現代化的元件庫

Shadcn/UI 不是傳統的元件庫,而是一個可以直接複製到專案中的元件集合。它的特點包括:

  • 完全可自訂:每個元件的原始碼都在你的專案中
  • 無障礙設計:所有元件都遵循 WCAG 標準
  • 與 Tailwind 完美整合:使用 Tailwind CSS 進行樣式設計

6. TypeScript - 類型安全的 JavaScript

TypeScript 為 JavaScript 添加了靜態類型檢查,提供:

  • 更少的執行時錯誤:在編譯時期就能發現大部分錯誤
  • 更好的開發體驗:智慧型程式碼提示和自動完成
  • 更容易重構:類型系統讓大規模重構變得更安全

詳細實作步驟

步驟 1:專案初始化與環境設置

首先,我們需要建立一個新的 Next.js 專案並設置所有必要的依賴項:

# 使用最新版本的 create-next-app 建立專案
npx create-next-app@latest my-store --typescript --tailwind --eslint --app --src-dir

# 進入專案目錄
cd my-store

# 安裝額外的依賴項
npm install @next/mdx @mdx-js/loader @mdx-js/react gray-matter reading-time
npm install zustand @tanstack/react-query
npm install framer-motion lucide-react
npm install @radix-ui/react-slot class-variance-authority clsx tailwind-merge

步驟 2:設置 Shadcn/UI

Shadcn/UI 的設置需要特別注意,因為它會直接影響整個專案的元件結構:

# 初始化 Shadcn/UI
npx shadcn@latest init

在初始化過程中,選擇以下選項:

  • TypeScript:Yes
  • 樣式:Default
  • 基礎顏色:Slate(或根據品牌選擇)
  • CSS 變數:Yes

接著安裝我們需要的元件:

# 安裝電商網站常用的元件
npx shadcn@latest add button card dialog sheet badge
npx shadcn@latest add input label textarea select
npx shadcn@latest add separator skeleton sonner
npx shadcn@latest add dropdown-menu navigation-menu

步驟 3:配置 MDX 支援

MDX 的配置對於管理產品內容至關重要。建立 next.config.mjs

import createMDX from '@next/mdx'
import remarkGfm from 'remark-gfm'
import rehypePrism from 'rehype-prism-plus'

const withMDX = createMDX({
  options: {
    remarkPlugins: [remarkGfm],
    rehypePlugins: [rehypePrism],
  },
})

/** @type {import('next').NextConfig} */
const nextConfig = {
  pageExtensions: ['ts', 'tsx', 'mdx'],
  images: {
    domains: ['images.unsplash.com', 'cdn.shopify.com'],
  },
  experimental: {
    mdxRs: true,
  },
}

export default withMDX(nextConfig)

建立 mdx-components.tsx 來定義全域 MDX 元件:

import type { MDXComponents } from 'mdx/types'
import Image from 'next/image'
import { Button } from '@/components/ui/button'
import { Card } from '@/components/ui/card'

export function useMDXComponents(components: MDXComponents): MDXComponents {
  return {
    ...components,
    h1: ({ children }) => (
      <h1 className="text-4xl font-bold tracking-tight mb-4">{children}</h1>
    ),
    h2: ({ children }) => (
      <h2 className="text-3xl font-semibold mb-3">{children}</h2>
    ),
    p: ({ children }) => (
      <p className="text-muted-foreground leading-7 mb-4">{children}</p>
    ),
    Image,
    Button,
    Card,
  }
}

步驟 4:建立專案結構

一個組織良好的專案結構對於維護和擴展至關重要:

src/
├── app/
│   ├── layout.tsx
│   ├── page.tsx
│   └── globals.css
├── components/
│   ├── ui/           # Shadcn/UI 元件
│   ├── store/        # 商店特定元件
│   │   ├── ProductCard.tsx
│   │   ├── ProductGrid.tsx
│   │   ├── CartSheet.tsx
│   │   ├── CheckoutDialog.tsx
│   │   └── SearchBar.tsx
│   └── layout/       # 佈局元件
│       ├── Header.tsx
│       ├── Footer.tsx
│       └── Hero.tsx
├── content/
│   └── products/     # MDX 產品檔案
│       ├── product-1.mdx
│       ├── product-2.mdx
│       └── product-3.mdx
├── lib/
│   ├── store.ts      # Zustand 狀態管理
│   ├── api.ts        # API 函數
│   └── utils.ts      # 工具函數
├── types/
│   └── index.ts      # TypeScript 類型定義
└── hooks/            # 自訂 Hooks
    ├── useCart.ts
    └── useProducts.ts

步驟 5:定義資料類型

建立 src/types/index.ts 來定義專案中使用的所有類型:

export interface Product {
  id: string
  name: string
  slug: string
  price: number
  salePrice?: number
  description: string
  shortDescription: string
  images: string[]
  category: string
  tags: string[]
  inStock: boolean
  quantity: number
  variants?: ProductVariant[]
  features: string[]
  specifications: Record<string, string>
  reviews: Review[]
  rating: number
  createdAt: Date
  updatedAt: Date
}

export interface ProductVariant {
  id: string
  name: string
  price: number
  image?: string
  inStock: boolean
  attributes: Record<string, string>
}

export interface Review {
  id: string
  userId: string
  userName: string
  rating: number
  comment: string
  createdAt: Date
  helpful: number
}

export interface CartItem {
  productId: string
  variantId?: string
  quantity: number
  product: Product
  variant?: ProductVariant
}

export interface ShippingInfo {
  fullName: string
  email: string
  phone: string
  address: string
  city: string
  state: string
  zipCode: string
  country: string
}

export interface Order {
  id: string
  items: CartItem[]
  shipping: ShippingInfo
  subtotal: number
  tax: number
  shipping: number
  total: number
  status: 'pending' | 'processing' | 'shipped' | 'delivered' | 'cancelled'
  createdAt: Date
}

步驟 6:實作狀態管理

使用 Zustand 建立全域狀態管理,建立 src/lib/store.ts

import { create } from 'zustand'
import { persist, createJSONStorage } from 'zustand/middleware'
import { CartItem, Product, ProductVariant } from '@/types'

interface CartStore {
  items: CartItem[]
  isOpen: boolean
  
  // 購物車操作
  addItem: (product: Product, variant?: ProductVariant, quantity?: number) => void
  removeItem: (productId: string, variantId?: string) => void
  updateQuantity: (productId: string, quantity: number, variantId?: string) => void
  clearCart: () => void
  
  // UI 操作
  openCart: () => void
  closeCart: () => void
  
  // 計算屬性
  getTotalItems: () => number
  getSubtotal: () => number
  getTax: (rate?: number) => number
  getTotal: (taxRate?: number) => number
}

export const useCart = create<CartStore>()(
  persist(
    (set, get) => ({
      items: [],
      isOpen: false,
      
      addItem: (product, variant, quantity = 1) => {
        set((state) => {
          const existingItem = state.items.find(
            item => item.productId === product.id && 
                   item.variantId === variant?.id
          )
          
          if (existingItem) {
            return {
              items: state.items.map(item =>
                item.productId === product.id && item.variantId === variant?.id
                  ? { ...item, quantity: item.quantity + quantity }
                  : item
              )
            }
          }
          
          return {
            items: [...state.items, {
              productId: product.id,
              variantId: variant?.id,
              quantity,
              product,
              variant
            }]
          }
        })
      },
      
      removeItem: (productId, variantId) => {
        set((state) => ({
          items: state.items.filter(
            item => !(item.productId === productId && item.variantId === variantId)
          )
        }))
      },
      
      updateQuantity: (productId, quantity, variantId) => {
        if (quantity <= 0) {
          get().removeItem(productId, variantId)
          return
        }
        
        set((state) => ({
          items: state.items.map(item =>
            item.productId === productId && item.variantId === variantId
              ? { ...item, quantity }
              : item
          )
        }))
      },
      
      clearCart: () => set({ items: [] }),
      openCart: () => set({ isOpen: true }),
      closeCart: () => set({ isOpen: false }),
      
      getTotalItems: () => {
        return get().items.reduce((total, item) => total + item.quantity, 0)
      },
      
      getSubtotal: () => {
        return get().items.reduce((total, item) => {
          const price = item.variant?.price ?? item.product.salePrice ?? item.product.price
          return total + (price * item.quantity)
        }, 0)
      },
      
      getTax: (rate = 0.08) => {
        return get().getSubtotal() * rate
      },
      
      getTotal: (taxRate = 0.08) => {
        const subtotal = get().getSubtotal()
        return subtotal + get().getTax(taxRate)
      }
    }),
    {
      name: 'shopping-cart',
      storage: createJSONStorage(() => localStorage),
    }
  )
)

步驟 7:建立核心元件

產品卡片元件

建立 src/components/store/ProductCard.tsx

'use client'

import Image from 'next/image'
import { useState } from 'react'
import { motion } from 'framer-motion'
import { ShoppingCart, Heart, Eye } from 'lucide-react'
import { Card, CardContent, CardFooter } from '@/components/ui/card'
import { Button } from '@/components/ui/button'
import { Badge } from '@/components/ui/badge'
import { useCart } from '@/lib/store'
import { Product } from '@/types'
import { cn } from '@/lib/utils'

interface ProductCardProps {
  product: Product
  onQuickView?: (product: Product) => void
}

export function ProductCard({ product, onQuickView }: ProductCardProps) {
  const [isHovered, setIsHovered] = useState(false)
  const [isFavorite, setIsFavorite] = useState(false)
  const { addItem, openCart } = useCart()
  
  const discount = product.salePrice 
    ? Math.round(((product.price - product.salePrice) / product.price) * 100)
    : 0
  
  const handleAddToCart = () => {
    addItem(product)
    openCart()
  }
  
  return (
    <motion.div
      initial={{ opacity: 0, y: 20 }}
      animate={{ opacity: 1, y: 0 }}
      transition={{ duration: 0.4 }}
      whileHover={{ y: -5 }}
    >
      <Card 
        className="relative overflow-hidden group cursor-pointer"
        onMouseEnter={() => setIsHovered(true)}
        onMouseLeave={() => setIsHovered(false)}
      >
        {/* 折扣標籤 */}
        {discount > 0 && (
          <Badge className="absolute top-2 left-2 z-10" variant="destructive">
            -{discount}%
          </Badge>
        )}
        
        {/* 收藏按鈕 */}
        <Button
          size="icon"
          variant="ghost"
          className="absolute top-2 right-2 z-10 opacity-0 group-hover:opacity-100 transition-opacity"
          onClick={() => setIsFavorite(!isFavorite)}
        >
          <Heart className={cn("h-4 w-4", isFavorite && "fill-current text-red-500")} />
        </Button>
        
        {/* 產品圖片 */}
        <div className="relative aspect-square overflow-hidden bg-gray-100">
          <Image
            src={product.images[0]}
            alt={product.name}
            fill
            className="object-cover transition-transform duration-300 group-hover:scale-110"
            sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
          />
          
          {/* 快速預覽按鈕 */}
          {isHovered && (
            <motion.div
              initial={{ opacity: 0 }}
              animate={{ opacity: 1 }}
              className="absolute inset-0 bg-black/40 flex items-center justify-center"
            >
              <Button
                variant="secondary"
                onClick={() => onQuickView?.(product)}
                className="gap-2"
              >
                <Eye className="h-4 w-4" />
                快速預覽
              </Button>
            </motion.div>
          )}
        </div>
        
        <CardContent className="p-4">
          <h3 className="font-medium text-lg mb-1 line-clamp-1">{product.name}</h3>
          <p className="text-sm text-muted-foreground mb-2 line-clamp-2">
            {product.shortDescription}
          </p>
          
          <div className="flex items-center gap-2 mb-2">
            {product.salePrice ? (
              <>
                <span className="font-bold text-lg">${product.salePrice}</span>
                <span className="text-sm text-muted-foreground line-through">
                  ${product.price}
                </span>
              </>
            ) : (
              <span className="font-bold text-lg">${product.price}</span>
            )}
          </div>
          
          {/* 評分 */}
          <div className="flex items-center gap-1">
            {[...Array(5)].map((_, i) => (
              <svg
                key={i}
                className={cn(
                  "h-4 w-4",
                  i < Math.floor(product.rating) 
                    ? "text-yellow-400 fill-current" 
                    : "text-gray-300"
                )}
                viewBox="0 0 20 20"
              >
                <path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z" />
              </svg>
            ))}
            <span className="text-sm text-muted-foreground ml-1">
              ({product.reviews.length})
            </span>
          </div>
        </CardContent>
        
        <CardFooter className="p-4 pt-0">
          <Button 
            className="w-full" 
            onClick={handleAddToCart}
            disabled={!product.inStock}
          >
            <ShoppingCart className="mr-2 h-4 w-4" />
            {product.inStock ? '加入購物車' : '缺貨中'}
          </Button>
        </CardFooter>
      </Card>
    </motion.div>
  )
}

購物車側邊欄元件

建立 src/components/store/CartSheet.tsx

'use client'

import Image from 'next/image'
import { Sheet, SheetContent, SheetHeader, SheetTitle } from '@/components/ui/sheet'
import { Button } from '@/components/ui/button'
import { Separator } from '@/components/ui/separator'
import { Badge } from '@/components/ui/badge'
import { ScrollArea } from '@/components/ui/scroll-area'
import { Minus, Plus, Trash2, ShoppingBag } from 'lucide-react'
import { useCart } from '@/lib/store'
import { motion, AnimatePresence } from 'framer-motion'

export function CartSheet() {
  const { 
    items, 
    isOpen, 
    closeCart, 
    updateQuantity, 
    removeItem,
    getTotalItems,
    getSubtotal,
    getTax,
    getTotal 
  } = useCart()
  
  const subtotal = getSubtotal()
  const tax = getTax()
  const total = getTotal()
  
  return (
    <Sheet open={isOpen} onOpenChange={closeCart}>
      <SheetContent className="w-full sm:max-w-md">
        <SheetHeader>
          <SheetTitle className="flex items-center gap-2">
            <ShoppingBag className="h-5 w-5" />
            購物車 ({getTotalItems()})
          </SheetTitle>
        </SheetHeader>
        
        {items.length === 0 ? (
          <div className="flex flex-col items-center justify-center h-[50vh] space-y-4">
            <ShoppingBag className="h-16 w-16 text-muted-foreground" />
            <p className="text-lg text-muted-foreground">您的購物車是空的</p>
            <Button onClick={closeCart}>繼續購物</Button>
          </div>
        ) : (
          <>
            <ScrollArea className="flex-1 mt-4 pr-4" style={{ height: 'calc(100vh - 280px)' }}>
              <AnimatePresence mode="popLayout">
                {items.map((item) => {
                  const price = item.variant?.price ?? item.product.salePrice ?? item.product.price
                  
                  return (
                    <motion.div
                      key={\`\${item.productId}-\${item.variantId}\`}
                      layout
                      initial={{ opacity: 0, x: -20 }}
                      animate={{ opacity: 1, x: 0 }}
                      exit={{ opacity: 0, x: 20 }}
                      className="flex gap-4 py-4 border-b"
                    >
                      <div className="relative h-24 w-24 rounded-md overflow-hidden bg-gray-100">
                        <Image
                          src={item.product.images[0]}
                          alt={item.product.name}
                          fill
                          className="object-cover"
                        />
                      </div>
                      
                      <div className="flex-1 space-y-2">
                        <h4 className="font-medium line-clamp-1">{item.product.name}</h4>
                        {item.variant && (
                          <p className="text-sm text-muted-foreground">
                            {item.variant.name}
                          </p>
                        )}
                        <div className="flex items-center justify-between">
                          <span className="font-medium">${price}</span>
                          
                          <div className="flex items-center gap-2">
                            <Button
                              size="icon"
                              variant="outline"
                              className="h-8 w-8"
                              onClick={() => updateQuantity(
                                item.productId, 
                                item.quantity - 1, 
                                item.variantId
                              )}
                            >
                              <Minus className="h-3 w-3" />
                            </Button>
                            
                            <span className="w-8 text-center">{item.quantity}</span>
                            
                            <Button
                              size="icon"
                              variant="outline"
                              className="h-8 w-8"
                              onClick={() => updateQuantity(
                                item.productId, 
                                item.quantity + 1, 
                                item.variantId
                              )}
                            >
                              <Plus className="h-3 w-3" />
                            </Button>
                            
                            <Button
                              size="icon"
                              variant="ghost"
                              className="h-8 w-8 text-destructive"
                              onClick={() => removeItem(item.productId, item.variantId)}
                            >
                              <Trash2 className="h-4 w-4" />
                            </Button>
                          </div>
                        </div>
                      </div>
                    </motion.div>
                  )
                })}
              </AnimatePresence>
            </ScrollArea>
            
            <div className="space-y-4 mt-6">
              <Separator />
              
              <div className="space-y-2">
                <div className="flex justify-between text-sm">
                  <span>小計</span>
                  <span>${subtotal.toFixed(2)}</span>
                </div>
                <div className="flex justify-between text-sm">
                  <span>稅金</span>
                  <span>${tax.toFixed(2)}</span>
                </div>
                <div className="flex justify-between text-sm">
                  <span>運費</span>
                  <Badge variant="secondary">免運費</Badge>
                </div>
                <Separator />
                <div className="flex justify-between font-semibold text-lg">
                  <span>總計</span>
                  <span>${total.toFixed(2)}</span>
                </div>
              </div>
              
              <Button className="w-full" size="lg">
                前往結帳
              </Button>
              
              <Button variant="outline" className="w-full" onClick={closeCart}>
                繼續購物
              </Button>
            </div>
          </>
        )}
      </SheetContent>
    </Sheet>
  )
}

步驟 8:建立真正的 One Page Store 內容結構

不同於傳統的產品描述,真正的 One Page Store 需要創造一個完整的產品宇宙。我們建立一個沉浸式的耳機體驗頁面:

---
id: "wavesound-pro"
name: "WaveSound Pro"
tagline: "重新定義你的聆聽體驗"
subtitle: "音質革命,從這裡開始"
price: 299.99
salePrice: 249.99
limitedOffer: "限時特價 - 48 小時內有效"
inStock: true
heroVideo: "/videos/wavesound-hero.mp4"
heroImage: "/images/wavesound-hero.jpg"
featureImages:
  - "/images/anc-demo.jpg"
  - "/images/battery-life.jpg"
  - "/images/comfort-design.jpg"
  - "/images/smart-features.jpg"
interactiveDemo: true
theme:
  primary: "from-purple-600 to-blue-600"
  accent: "#8B5CF6"
  background: "#0F0F23"
---

import { InteractiveAudioDemo } from '@/components/store/InteractiveAudioDemo'
import { FeatureShowcase } from '@/components/store/FeatureShowcase'
import { CustomerReviews } from '@/components/store/CustomerReviews'
import { PurchaseSection } from '@/components/store/PurchaseSection'

# 🎧 沉浸式音質革命

想像一下,當世界安靜下來,只剩下純粹的音樂在你耳邊流淌...

**WaveSound Pro** 不只是一副耳機,它是你進入**另一個世界**的門戶。搭載革命性的主動降噪技術,讓你在喧囂的城市中找到屬於自己的寧靜空間。

## 🔇 體驗真正的寧靜

<InteractiveAudioDemo 
  demoTracks={[
    { name: '一般模式', file: '/audio/normal-mode.mp3' },
    { name: '降噪開啟', file: '/audio/anc-mode.mp3' }
  ]}
  visualizer={true}
/>

*點擊上方按鈕,親自體驗降噪技術的神奇效果*

## ✨ 四大核心突破

<FeatureShowcase features={[
  {
    emoji: "🔇",
    title: "AI 智慧降噪",
    description: "95% 環境噪音消除,讓世界安靜下來",
    interactive: "hover:scale-105 transform transition-all duration-300",
    demo: "toggleNoise"
  },
  {
    emoji: "🔋", 
    title: "超長續航",
    description: "30 小時不間斷音樂,快充 15 分鐘續航 5 小時",
    interactive: "animate-pulse",
    counter: { from: 0, to: 30, suffix: "小時" }
  },
  {
    emoji: "🎵",
    title: "Hi-Res 音質",
    description: "錄音室級別音質,每個細節都清晰可聞",
    waveform: true
  },
  {
    emoji: "⚡",
    title: "瞬間連接",
    description: "藍牙 5.0 技術,0.1 秒極速配對",
    animation: "connectDevices"
  }
]} />

## 🌟 真實用戶體驗

<CustomerReviews reviews={[
  {
    avatar: "/images/user-1.jpg",
    name: "李小明",
    rating: 5,
    comment: "簡直是我用過最棒的耳機!降噪效果讓我在飛機上也能安靜休息。",
    verified: true,
    date: "2025-06-01"
  },
  {
    avatar: "/images/user-2.jpg", 
    name: "陳美麗",
    rating: 5,
    comment: "音質超乎想像,聽古典樂的層次感非常豐富,而且戴起來很舒適。",
    verified: true,
    date: "2025-05-28"
  },
  {
    avatar: "/images/user-3.jpg",
    name: "王大衛", 
    rating: 5,
    comment: "工作時戴著它,完全沒有外界干擾,效率提升了很多!",
    verified: true,
    date: "2025-05-25"
  }
]} animated={true} />

## 🛒 立即擁有你的音質革命

<PurchaseSection 
  product={{
    name: "WaveSound Pro",
    originalPrice: 299.99,
    salePrice: 249.99,
    savings: 50,
    urgency: "僅剩 23 個 - 48 小時限時特價",
    guarantees: [
      "30 天無條件退款",
      "2 年全球保固", 
      "免費全球配送"
    ],
    bonuses: [
      "價值 $49 的專業收納盒",
      "價值 $29 的飛機轉接頭"
    ]
  }}
  urgencyTimer={true}
  oneClick={true}
/>

互動式元件實作

讓我們建立幾個關鍵的互動元件來實現真正的 One Page Store 體驗:

1. 音質對比體驗元件

// src/components/store/InteractiveAudioDemo.tsx
'use client'

import { useState, useRef } from 'react'
import { motion } from 'framer-motion'
import { Play, Pause, Volume2, VolumeX } from 'lucide-react'
import { Button } from '@/components/ui/button'
import { Card } from '@/components/ui/card'

interface AudioDemoProps {
  demoTracks: { name: string; file: string }[]
  visualizer?: boolean
}

export function InteractiveAudioDemo({ demoTracks, visualizer }: AudioDemoProps) {
  const [activeTrack, setActiveTrack] = useState(0)
  const [isPlaying, setIsPlaying] = useState(false)
  const [audioLevel, setAudioLevel] = useState(0)
  const audioRef = useRef<HTMLAudioElement>(null)
  
  const togglePlay = async () => {
    if (!audioRef.current) return
    
    if (isPlaying) {
      audioRef.current.pause()
      setIsPlaying(false)
    } else {
      await audioRef.current.play()
      setIsPlaying(true)
    }
  }
  
  const switchTrack = (index: number) => {
    setActiveTrack(index)
    setIsPlaying(false)
    if (audioRef.current) {
      audioRef.current.src = demoTracks[index].file
    }
  }
  
  return (
    <Card className="p-8 bg-gradient-to-br from-gray-900 to-black text-white">
      <div className="text-center mb-6">
        <h3 className="text-2xl font-bold mb-2">🎧 親自體驗音質差異</h3>
        <p className="text-gray-300">戴上耳機,感受降噪技術的魔力</p>
      </div>
      
      {/* 音軌選擇 */}
      <div className="flex gap-4 justify-center mb-8">
        {demoTracks.map((track, index) => (
          <Button
            key={index}
            variant={activeTrack === index ? "default" : "outline"}
            onClick={() => switchTrack(index)}
            className={`px-6 py-3 transition-all duration-300 ${
              activeTrack === index 
                ? 'bg-purple-600 hover:bg-purple-700 transform scale-105' 
                : 'border-purple-400 text-purple-400 hover:bg-purple-600/20'
            }`}
          >
            {activeTrack === index && isPlaying ? (
              <VolumeX className="mr-2 h-4 w-4 animate-pulse" />
            ) : (
              <Volume2 className="mr-2 h-4 w-4" />
            )}
            {track.name}
          </Button>
        ))}
      </div>
      
      {/* 播放控制 */}
      <div className="flex justify-center mb-6">
        <motion.div
          whileHover={{ scale: 1.05 }}
          whileTap={{ scale: 0.95 }}
        >
          <Button
            size="lg"
            onClick={togglePlay}
            className="w-16 h-16 rounded-full bg-gradient-to-r from-purple-600 to-blue-600 hover:from-purple-700 hover:to-blue-700"
          >
            {isPlaying ? (
              <Pause className="h-6 w-6" />
            ) : (
              <Play className="h-6 w-6 ml-1" />
            )}
          </Button>
        </motion.div>
      </div>
      
      {/* 視覺化效果 */}
      {visualizer && (
        <div className="flex justify-center items-end gap-1 h-16 mb-4">
          {[...Array(20)].map((_, i) => (
            <motion.div
              key={i}
              className="bg-gradient-to-t from-purple-600 to-blue-400 w-2 rounded-full"
              style={{
                height: isPlaying ? `${Math.random() * 60 + 10}px` : '10px'
              }}
              animate={{
                height: isPlaying 
                  ? [`${Math.random() * 60 + 10}px`, `${Math.random() * 60 + 10}px`]
                  : '10px'
              }}
              transition={{
                duration: 0.5,
                repeat: isPlaying ? Infinity : 0,
                ease: "easeInOut"
              }}
            />
          ))}
        </div>
      )}
      
      <audio
        ref={audioRef}
        src={demoTracks[activeTrack]?.file}
        onEnded={() => setIsPlaying(false)}
        onTimeUpdate={(e) => {
          const audio = e.target as HTMLAudioElement
          setAudioLevel(audio.currentTime / audio.duration * 100)
        }}
      />
    </Card>
  )
}

步驟 9:實作產品載入系統

建立 src/lib/products.ts 來處理 MDX 產品檔案的載入:

import fs from 'fs'
import path from 'path'
import matter from 'gray-matter'
import { Product } from '@/types'

const productsDirectory = path.join(process.cwd(), 'src/content/products')

export async function getProductSlugs() {
  const files = fs.readdirSync(productsDirectory)
  return files.map(file => file.replace(/\.mdx$/, ''))
}

export async function getProductBySlug(slug: string): Promise<Product> {
  const fullPath = path.join(productsDirectory, \`\${slug}.mdx\`)
  const fileContents = fs.readFileSync(fullPath, 'utf8')
  const { data, content } = matter(fileContents)
  
  // 計算評分(示例:隨機生成或從評論計算)
  const rating = data.reviews 
    ? data.reviews.reduce((acc: number, review: any) => acc + review.rating, 0) / data.reviews.length
    : 4.5
  
  return {
    ...data,
    content,
    rating,
    reviews: data.reviews || [],
    createdAt: new Date(data.createdAt || Date.now()),
    updatedAt: new Date(data.updatedAt || Date.now()),
  } as Product
}

export async function getAllProducts(): Promise<Product[]> {
  const slugs = await getProductSlugs()
  const products = await Promise.all(
    slugs.map(slug => getProductBySlug(slug))
  )
  
  return products.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime())
}

export async function getProductsByCategory(category: string): Promise<Product[]> {
  const products = await getAllProducts()
  return products.filter(product => product.category === category)
}

export async function getFeaturedProducts(): Promise<Product[]> {
  const products = await getAllProducts()
  // 選擇有折扣或評分高的產品作為精選
  return products
    .filter(product => product.salePrice || product.rating >= 4.5)
    .slice(0, 6)
}

export async function searchProducts(query: string): Promise<Product[]> {
  const products = await getAllProducts()
  const lowercaseQuery = query.toLowerCase()
  
  return products.filter(product => 
    product.name.toLowerCase().includes(lowercaseQuery) ||
    product.description.toLowerCase().includes(lowercaseQuery) ||
    product.tags.some(tag => tag.toLowerCase().includes(lowercaseQuery))
  )
}

2. 震撼的 Hero 區塊元件

// src/components/store/HeroSection.tsx
'use client'

import { useState, useEffect } from 'react'
import { motion } from 'framer-motion'
import { Play, ShoppingCart, Clock, Star } from 'lucide-react'
import { Button } from '@/components/ui/button'
import { Badge } from '@/components/ui/badge'

export function HeroSection() {
  const [timeLeft, setTimeLeft] = useState({
    hours: 47,
    minutes: 32,
    seconds: 15
  })
  
  useEffect(() => {
    const timer = setInterval(() => {
      setTimeLeft(prev => {
        if (prev.seconds > 0) {
          return { ...prev, seconds: prev.seconds - 1 }
        } else if (prev.minutes > 0) {
          return { ...prev, minutes: prev.minutes - 1, seconds: 59 }
        } else if (prev.hours > 0) {
          return { hours: prev.hours - 1, minutes: 59, seconds: 59 }
        }
        return prev
      })
    }, 1000)
    
    return () => clearInterval(timer)
  }, [])
  
  return (
    <section className="relative min-h-screen bg-gradient-to-br from-gray-900 via-purple-900 to-black overflow-hidden">
      {/* 背景動態效果 */}
      <div className="absolute inset-0">
        <div className="absolute top-1/4 left-1/4 w-96 h-96 bg-purple-500/20 rounded-full blur-3xl animate-pulse" />
        <div className="absolute bottom-1/4 right-1/4 w-96 h-96 bg-blue-500/20 rounded-full blur-3xl animate-pulse delay-1000" />
      </div>
      
      <div className="relative z-10 container mx-auto px-4 py-20 text-center text-white">
        {/* 限時優惠標籤 */}
        <motion.div
          initial={{ opacity: 0, y: -20 }}
          animate={{ opacity: 1, y: 0 }}
          className="mb-6"
        >
          <Badge className="bg-red-600 hover:bg-red-700 text-white px-4 py-2 text-sm font-bold">
            ⚡ 限時特價 - 立省 $50!
          </Badge>
        </motion.div>
        
        {/* 主標題 */}
        <motion.h1
          initial={{ opacity: 0, y: 30 }}
          animate={{ opacity: 1, y: 0 }}
          transition={{ delay: 0.2 }}
          className="text-6xl md:text-8xl font-black mb-6"
        >
          <span className="bg-gradient-to-r from-purple-400 via-pink-500 to-blue-500 bg-clip-text text-transparent">
            WaveSound
          </span>
          <br />
          <span className="text-white">Pro</span>
        </motion.h1>
        
        {/* 副標題 */}
        <motion.p
          initial={{ opacity: 0, y: 20 }}
          animate={{ opacity: 1, y: 0 }}
          transition={{ delay: 0.4 }}
          className="text-xl md:text-2xl mb-4 font-light"
        >
          重新定義你的聆聽體驗
        </motion.p>
        
        <motion.p
          initial={{ opacity: 0, y: 20 }}
          animate={{ opacity: 1, y: 0 }}
          transition={{ delay: 0.6 }}
          className="text-lg text-gray-300 mb-8 max-w-2xl mx-auto"
        >
          音質革命,從這裡開始。95% 降噪效果,30 小時續航,讓你沉浸在純粹的音樂世界中。
        </motion.p>
        
        {/* 倒數計時器 */}
        <motion.div
          initial={{ opacity: 0, scale: 0.8 }}
          animate={{ opacity: 1, scale: 1 }}
          transition={{ delay: 0.8 }}
          className="flex justify-center gap-4 mb-8"
        >
          <div className="bg-black/50 backdrop-blur-md rounded-lg p-4 text-center">
            <div className="text-3xl font-bold text-red-400">{timeLeft.hours}</div>
            <div className="text-xs text-gray-400">小時</div>
          </div>
          <div className="bg-black/50 backdrop-blur-md rounded-lg p-4 text-center">
            <div className="text-3xl font-bold text-red-400">{timeLeft.minutes}</div>
            <div className="text-xs text-gray-400">分鐘</div>
          </div>
          <div className="bg-black/50 backdrop-blur-md rounded-lg p-4 text-center">
            <div className="text-3xl font-bold text-red-400">{timeLeft.seconds}</div>
            <div className="text-xs text-gray-400"></div>
          </div>
        </motion.div>
        
        {/* 價格展示 */}
        <motion.div
          initial={{ opacity: 0, y: 20 }}
          animate={{ opacity: 1, y: 0 }}
          transition={{ delay: 1.0 }}
          className="mb-8"
        >
          <div className="flex items-center justify-center gap-4 mb-4">
            <span className="text-4xl font-bold text-green-400">$249.99</span>
            <span className="text-2xl text-gray-500 line-through">$299.99</span>
            <Badge className="bg-green-600 text-white">省 $50</Badge>
          </div>
          <div className="flex items-center justify-center gap-2 text-yellow-400">
            {[...Array(5)].map((_, i) => (
              <Star key={i} className="h-5 w-5 fill-current" />
            ))}
            <span className="text-white ml-2">4.9/5 (2,847 評價)</span>
          </div>
        </motion.div>
        
        {/* 行動按鈕 */}
        <motion.div
          initial={{ opacity: 0, y: 20 }}
          animate={{ opacity: 1, y: 0 }}
          transition={{ delay: 1.2 }}
          className="flex flex-col sm:flex-row gap-4 justify-center items-center"
        >
          <Button
            size="lg"
            className="bg-gradient-to-r from-purple-600 to-blue-600 hover:from-purple-700 hover:to-blue-700 text-white px-8 py-4 text-lg font-bold transform hover:scale-105 transition-all duration-300 shadow-lg"
          >
            <ShoppingCart className="mr-2 h-5 w-5" />
            立即購買 - 免費送貨
          </Button>
          
          <Button
            size="lg"
            variant="outline"
            className="border-white text-white hover:bg-white hover:text-black px-8 py-4 text-lg font-semibold"
          >
            <Play className="mr-2 h-5 w-5" />
            觀看展示影片
          </Button>
        </motion.div>
        
        {/* 信任標識 */}
        <motion.div
          initial={{ opacity: 0 }}
          animate={{ opacity: 1 }}
          transition={{ delay: 1.4 }}
          className="flex flex-wrap justify-center gap-6 mt-12 text-sm text-gray-400"
        >
          <div className="flex items-center gap-2">
            <Clock className="h-4 w-4" />
            <span>30 天退貨保證</span>
          </div>
          <div className="flex items-center gap-2">
            <ShoppingCart className="h-4 w-4" />
            <span>全球免費配送</span>
          </div>
          <div className="flex items-center gap-2">
            <Star className="h-4 w-4" />
            <span>2 年保固</span>
          </div>
        </motion.div>
      </div>
      
      {/* 產品圖片 */}
      <motion.div
        initial={{ opacity: 0, scale: 0.8 }}
        animate={{ opacity: 1, scale: 1 }}
        transition={{ delay: 0.5, duration: 1 }}
        className="absolute bottom-0 right-0 w-1/2 h-1/2 bg-gradient-to-tl from-purple-500/20 to-transparent"
      >
        <img
          src="/images/wavesound-hero.png"
          alt="WaveSound Pro"
          className="w-full h-full object-contain transform rotate-12 hover:rotate-6 transition-transform duration-700"
        />
      </motion.div>
    </section>
  )
}

3. 購買轉換區塊元件

// src/components/store/PurchaseSection.tsx
'use client'

import { useState } from 'react'
import { motion } from 'framer-motion'
import { ShoppingCart, Shield, Truck, RotateCcw, Gift, Clock } from 'lucide-react'
import { Button } from '@/components/ui/button'
import { Card } from '@/components/ui/card'
import { Badge } from '@/components/ui/badge'
import { useCart } from '@/lib/store'

interface PurchaseSectionProps {
  product: {
    name: string
    originalPrice: number
    salePrice: number
    savings: number
    urgency: string
    guarantees: string[]
    bonuses: string[]
  }
  urgencyTimer?: boolean
  oneClick?: boolean
}

export function PurchaseSection({ product, urgencyTimer, oneClick }: PurchaseSectionProps) {
  const [isAdding, setIsAdding] = useState(false)
  const { addItem, openCart } = useCart()
  
  const handlePurchase = async () => {
    setIsAdding(true)
    
    // 模擬加入購物車的過程
    await new Promise(resolve => setTimeout(resolve, 1000))
    
    // 這裡應該加入實際的產品物件
    // addItem(productData)
    
    if (oneClick) {
      // 直接跳轉到結帳頁面
      window.location.href = '/checkout'
    } else {
      openCart()
    }
    
    setIsAdding(false)
  }
  
  return (
    <section className="py-20 bg-gradient-to-br from-purple-900 via-black to-gray-900">
      <div className="container mx-auto px-4">
        <motion.div
          initial={{ opacity: 0, y: 50 }}
          whileInView={{ opacity: 1, y: 0 }}
          transition={{ duration: 0.8 }}
          className="max-w-4xl mx-auto"
        >
          <Card className="p-8 md:p-12 bg-black/50 backdrop-blur-xl border-purple-500/30 text-white">
            {/* 緊急性提醒 */}
            <div className="text-center mb-8">
              <Badge className="bg-red-600 text-white px-4 py-2 mb-4 animate-pulse">
                🔥 {product.urgency}
              </Badge>
              <h2 className="text-4xl md:text-5xl font-bold mb-4">
                立即擁有你的
                <span className="bg-gradient-to-r from-purple-400 to-blue-400 bg-clip-text text-transparent">
                  音質革命
                </span>
              </h2>
            </div>
            
            <div className="grid md:grid-cols-2 gap-8 items-center">
              {/* 左側:價格和保證 */}
              <div className="space-y-6">
                {/* 價格展示 */}
                <div className="text-center md:text-left">
                  <div className="flex items-baseline gap-4 mb-2">
                    <span className="text-5xl font-bold text-green-400">
                      ${product.salePrice}
                    </span>
                    <span className="text-2xl text-gray-500 line-through">
                      ${product.originalPrice}
                    </span>
                  </div>
                  <p className="text-xl text-green-400 font-semibold">
                    立省 ${product.savings}!
                  </p>
                </div>
                
                {/* 保證項目 */}
                <div className="space-y-3">
                  <h3 className="text-xl font-semibold mb-3 flex items-center gap-2">
                    <Shield className="h-5 w-5 text-green-400" />
                    無風險保證
                  </h3>
                  {product.guarantees.map((guarantee, index) => (
                    <div key={index} className="flex items-center gap-3">
                      <div className="w-2 h-2 bg-green-400 rounded-full" />
                      <span>{guarantee}</span>
                    </div>
                  ))}
                </div>
                
                {/* 額外贈品 */}
                <div className="space-y-3">
                  <h3 className="text-xl font-semibold mb-3 flex items-center gap-2">
                    <Gift className="h-5 w-5 text-purple-400" />
                    限時免費贈品
                  </h3>
                  {product.bonuses.map((bonus, index) => (
                    <div key={index} className="flex items-center gap-3">
                      <div className="w-2 h-2 bg-purple-400 rounded-full" />
                      <span>{bonus}</span>
                    </div>
                  ))}
                </div>
              </div>
              
              {/* 右側:購買按鈕和特色 */}
              <div className="space-y-6">
                {/* 主要購買按鈕 */}
                <motion.div
                  whileHover={{ scale: 1.02 }}
                  whileTap={{ scale: 0.98 }}
                >
                  <Button
                    size="lg"
                    onClick={handlePurchase}
                    disabled={isAdding}
                    className="w-full py-6 text-xl font-bold bg-gradient-to-r from-purple-600 to-blue-600 hover:from-purple-700 hover:to-blue-700 transform transition-all duration-300 shadow-lg hover:shadow-xl"
                  >
                    {isAdding ? (
                      <div className="flex items-center gap-2">
                        <div className="w-5 h-5 border-2 border-white border-t-transparent rounded-full animate-spin" />
                        處理中...
                      </div>
                    ) : (
                      <div className="flex items-center gap-2">
                        <ShoppingCart className="h-6 w-6" />
                        {oneClick ? '一鍵購買' : '加入購物車'}
                      </div>
                    )}
                  </Button>
                </motion.div>
                
                {/* 信任標識 */}
                <div className="grid grid-cols-3 gap-4 text-center">
                  <div className="flex flex-col items-center gap-2">
                    <Truck className="h-8 w-8 text-green-400" />
                    <span className="text-sm">免費送貨</span>
                  </div>
                  <div className="flex flex-col items-center gap-2">
                    <RotateCcw className="h-8 w-8 text-blue-400" />
                    <span className="text-sm">30天退貨</span>
                  </div>
                  <div className="flex flex-col items-center gap-2">
                    <Shield className="h-8 w-8 text-purple-400" />
                    <span className="text-sm">2年保固</span>
                  </div>
                </div>
                
                {/* 社會證明 */}
                <div className="bg-gray-800/50 rounded-lg p-4 text-center">
                  <p className="text-sm text-gray-300 mb-2">
                    已有 <span className="text-green-400 font-bold">12,847</span> 人購買
                  </p>
                  <div className="flex justify-center gap-1">
                    {[...Array(5)].map((_, i) => (
                      <span key={i} className="text-yellow-400"></span>
                    ))}
                    <span className="ml-2 text-sm">4.9/5 平均評分</span>
                  </div>
                </div>
                
                {urgencyTimer && (
                  <div className="bg-red-900/30 border border-red-500/50 rounded-lg p-4 text-center">
                    <div className="flex items-center justify-center gap-2 mb-2">
                      <Clock className="h-5 w-5 text-red-400" />
                      <span className="text-red-400 font-bold">限時優惠即將結束</span>
                    </div>
                    <p className="text-sm text-gray-300">
                      錯過這次優惠,下次恢復原價 ${product.originalPrice}
                    </p>
                  </div>
                )}
              </div>
            </div>
          </Card>
        </motion.div>
      </div>
    </section>
  )
}

步驟 10:建立完整的 One Page Store 頁面

整合所有元件,建立真正的 One Page Store src/app/page.tsx

import { Suspense } from 'react'
import { HeroSection } from '@/components/store/HeroSection'
import { InteractiveAudioDemo } from '@/components/store/InteractiveAudioDemo'
import { FeatureShowcase } from '@/components/store/FeatureShowcase'
import { CustomerReviews } from '@/components/store/CustomerReviews'
import { PurchaseSection } from '@/components/store/PurchaseSection'
import { FAQSection } from '@/components/store/FAQSection'
import { TrustBadges } from '@/components/store/TrustBadges'
import { SocialProof } from '@/components/store/SocialProof'

// 產品資料
const productData = {
  name: "WaveSound Pro",
  originalPrice: 299.99,
  salePrice: 249.99,
  savings: 50,
  urgency: "僅剩 23 個 - 48 小時限時特價",
  guarantees: [
    "30 天無條件退款",
    "2 年全球保固", 
    "免費全球配送"
  ],
  bonuses: [
    "價值 $49 的專業收納盒",
    "價值 $29 的飛機轉接頭"
  ]
}

const audioTracks = [
  { name: '一般模式', file: '/audio/normal-mode.mp3' },
  { name: '降噪開啟', file: '/audio/anc-mode.mp3' }
]

const features = [
  {
    emoji: "🔇",
    title: "AI 智慧降噪",
    description: "95% 環境噪音消除,讓世界安靜下來",
    interactive: "hover:scale-105 transform transition-all duration-300",
    demo: "toggleNoise"
  },
  {
    emoji: "🔋", 
    title: "超長續航",
    description: "30 小時不間斷音樂,快充 15 分鐘續航 5 小時",
    interactive: "animate-pulse",
    counter: { from: 0, to: 30, suffix: "小時" }
  },
  {
    emoji: "🎵",
    title: "Hi-Res 音質",
    description: "錄音室級別音質,每個細節都清晰可聞",
    waveform: true
  },
  {
    emoji: "⚡",
    title: "瞬間連接",
    description: "藍牙 5.0 技術,0.1 秒極速配對",
    animation: "connectDevices"
  }
]

const reviews = [
  {
    avatar: "/images/user-1.jpg",
    name: "李小明",
    rating: 5,
    comment: "簡直是我用過最棒的耳機!降噪效果讓我在飛機上也能安靜休息。",
    verified: true,
    date: "2025-06-01"
  },
  {
    avatar: "/images/user-2.jpg", 
    name: "陳美麗",
    rating: 5,
    comment: "音質超乎想像,聽古典樂的層次感非常豐富,而且戴起來很舒適。",
    verified: true,
    date: "2025-05-28"
  },
  {
    avatar: "/images/user-3.jpg",
    name: "王大衛", 
    rating: 5,
    comment: "工作時戴著它,完全沒有外界干擾,效率提升了很多!",
    verified: true,
    date: "2025-05-25"
  }
]

export default function OnePageStore() {
  return (
    <main className="min-h-screen">
      {/* 1. 震撼的 Hero 區塊 */}
      <HeroSection />
      
      {/* 2. 互動式音質體驗 */}
      <section className="py-20 bg-gray-900">
        <div className="container mx-auto px-4">
          <div className="text-center mb-12">
            <h2 className="text-4xl font-bold text-white mb-4">
              🔇 體驗真正的寧靜
            </h2>
            <p className="text-xl text-gray-300">
              親自感受 95% 降噪效果的魔力
            </p>
          </div>
          <InteractiveAudioDemo 
            demoTracks={audioTracks}
            visualizer={true}
          />
        </div>
      </section>
      
      {/* 3. 四大核心功能展示 */}
      <section className="py-20 bg-black">
        <div className="container mx-auto px-4">
          <div className="text-center mb-12">
            <h2 className="text-4xl font-bold text-white mb-4">
              ✨ 四大核心突破
            </h2>
            <p className="text-xl text-gray-300">
              革命性技術,重新定義音質體驗
            </p>
          </div>
          <FeatureShowcase features={features} />
        </div>
      </section>
      
      {/* 4. 社會證明 - 真實用戶評價 */}
      <section className="py-20 bg-gradient-to-br from-purple-900 to-gray-900">
        <div className="container mx-auto px-4">
          <div className="text-center mb-12">
            <h2 className="text-4xl font-bold text-white mb-4">
              🌟 12,847 位用戶的選擇
            </h2>
            <p className="text-xl text-gray-300">
              聽聽他們怎麼說
            </p>
          </div>
          <CustomerReviews reviews={reviews} animated={true} />
        </div>
      </section>
      
      {/* 5. 社會證明數據 */}
      <SocialProof />
      
      {/* 6. FAQ 常見問題 */}
      <FAQSection />
      
      {/* 7. 最終購買轉換區 */}
      <PurchaseSection 
        product={productData}
        urgencyTimer={true}
        oneClick={true}
      />
      
      {/* 8. 信任標識 */}
      <TrustBadges />
    </main>
  )
}

步驟 11:效能優化

1. 圖片優化

使用 Next.js 的 Image 元件和適當的載入策略:

// 建立圖片載入元件
import Image from 'next/image'
import { useState } from 'react'
import { cn } from '@/lib/utils'

interface OptimizedImageProps {
  src: string
  alt: string
  className?: string
  priority?: boolean
}

export function OptimizedImage({ src, alt, className, priority }: OptimizedImageProps) {
  const [isLoading, setIsLoading] = useState(true)
  
  return (
    <div className={cn("relative overflow-hidden bg-gray-100", className)}>
      <Image
        src={src}
        alt={alt}
        fill
        priority={priority}
        className={cn(
          "object-cover duration-700 ease-in-out",
          isLoading ? "scale-110 blur-2xl grayscale" : "scale-100 blur-0 grayscale-0"
        )}
        onLoadingComplete={() => setIsLoading(false)}
        sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw"
      />
    </div>
  )
}

2. 程式碼分割和延遲載入

// 使用動態導入進行程式碼分割
import dynamic from 'next/dynamic'

// 延遲載入大型元件
const ProductQuickView = dynamic(
  () => import('@/components/store/ProductQuickView'),
  { 
    loading: () => <ProductQuickViewSkeleton />,
    ssr: false 
  }
)

const ReviewSection = dynamic(
  () => import('@/components/store/ReviewSection'),
  { loading: () => <ReviewSectionSkeleton /> }
)

3. 資料預取和快取

使用 React Query 進行資料管理:

// src/hooks/useProducts.ts
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
import { getAllProducts, searchProducts } from '@/lib/products'

export function useProducts() {
  return useQuery({
    queryKey: ['products'],
    queryFn: getAllProducts,
    staleTime: 5 * 60 * 1000, // 5 分鐘
    cacheTime: 10 * 60 * 1000, // 10 分鐘
  })
}

export function useProductSearch(query: string) {
  return useQuery({
    queryKey: ['products', 'search', query],
    queryFn: () => searchProducts(query),
    enabled: query.length > 2,
    staleTime: 2 * 60 * 1000,
  })
}

// 預取產品資料
export function usePrefetchProducts() {
  const queryClient = useQueryClient()
  
  return () => {
    queryClient.prefetchQuery({
      queryKey: ['products'],
      queryFn: getAllProducts,
    })
  }
}

步驟 12:SEO 優化

建立 SEO 元件和結構化資料:

// src/components/seo/ProductJsonLd.tsx
export function ProductJsonLd({ product }: { product: Product }) {
  const jsonLd = {
    '@context': 'https://schema.org',
    '@type': 'Product',
    name: product.name,
    description: product.description,
    image: product.images,
    offers: {
      '@type': 'Offer',
      price: product.salePrice || product.price,
      priceCurrency: 'TWD',
      availability: product.inStock 
        ? 'https://schema.org/InStock' 
        : 'https://schema.org/OutOfStock',
    },
    aggregateRating: {
      '@type': 'AggregateRating',
      ratingValue: product.rating,
      reviewCount: product.reviews.length,
    },
  }
  
  return (
    <script
      type="application/ld+json"
      dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
    />
  )
}

步驟 13:部署準備

環境變數設置

建立 .env.local

# API 端點
NEXT_PUBLIC_API_URL=https://api.yourstore.com

# 第三方服務
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_xxx
STRIPE_SECRET_KEY=sk_test_xxx

# 分析工具
NEXT_PUBLIC_GA_ID=G-XXXXXXXXXX
NEXT_PUBLIC_FACEBOOK_PIXEL_ID=xxxxxxxxxxxxx

# 圖片 CDN
NEXT_PUBLIC_IMAGE_CDN=https://cdn.yourstore.com

建置優化配置

更新 next.config.mjs

const nextConfig = {
  // ... 其他配置
  
  // 生產環境優化
  swcMinify: true,
  compress: true,
  
  // 圖片優化
  images: {
    formats: ['image/avif', 'image/webp'],
    minimumCacheTTL: 60,
  },
  
  // 實驗性功能
  experimental: {
    optimizeCss: true,
    optimizePackageImports: ['lucide-react', 'framer-motion'],
  },
  
  // HTTP 標頭
  async headers() {
    return [
      {
        source: '/:path*',
        headers: [
          {
            key: 'X-DNS-Prefetch-Control',
            value: 'on'
          },
          {
            key: 'X-Frame-Options',
            value: 'SAMEORIGIN'
          },
        ],
      },
    ]
  },
}

🚀 One Page Store 進階優化技巧

1. 即時搜尋功能

實作一個快速、響應式的搜尋功能對於提升使用者體驗至關重要:

// src/components/store/SearchCommand.tsx
'use client'

import { useEffect, useState } from 'react'
import { Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from '@/components/ui/command'
import { useDebounce } from '@/hooks/useDebounce'
import { Product } from '@/types'
import { Search } from 'lucide-react'

export function SearchCommand({ products }: { products: Product[] }) {
  const [open, setOpen] = useState(false)
  const [search, setSearch] = useState('')
  const debouncedSearch = useDebounce(search, 300)
  
  const filteredProducts = products.filter(product =>
    product.name.toLowerCase().includes(debouncedSearch.toLowerCase()) ||
    product.description.toLowerCase().includes(debouncedSearch.toLowerCase())
  )
  
  useEffect(() => {
    const down = (e: KeyboardEvent) => {
      if (e.key === 'k' && (e.metaKey || e.ctrlKey)) {
        e.preventDefault()
        setOpen((open) => !open)
      }
    }
    
    document.addEventListener('keydown', down)
    return () => document.removeEventListener('keydown', down)
  }, [])
  
  return (
    <CommandDialog open={open} onOpenChange={setOpen}>
      <CommandInput 
        placeholder="搜尋產品..." 
        value={search}
        onValueChange={setSearch}
      />
      <CommandList>
        <CommandEmpty>找不到相關產品</CommandEmpty>
        <CommandGroup heading="產品">
          {filteredProducts.slice(0, 5).map((product) => (
            <CommandItem
              key={product.id}
              onSelect={() => {
                // 導航到產品頁面
                window.location.href = \`/products/\${product.slug}\`
                setOpen(false)
              }}
            >
              <Search className="mr-2 h-4 w-4" />
              <span>{product.name}</span>
            </CommandItem>
          ))}
        </CommandGroup>
      </CommandList>
    </CommandDialog>
  )
}

2. 進階篩選系統

建立多維度的產品篩選功能:

// src/components/store/ProductFilters.tsx
export function ProductFilters({ 
  categories, 
  priceRange, 
  onFilterChange 
}: ProductFiltersProps) {
  const [filters, setFilters] = useState({
    category: 'all',
    minPrice: 0,
    maxPrice: 9999,
    inStock: false,
    sortBy: 'featured'
  })
  
  return (
    <aside className="w-64 space-y-6">
      {/* 分類篩選 */}
      <div>
        <h3 className="font-semibold mb-3">商品分類</h3>
        <RadioGroup
          value={filters.category}
          onValueChange={(value) => {
            setFilters({ ...filters, category: value })
            onFilterChange({ ...filters, category: value })
          }}
        >
          <div className="space-y-2">
            <Label className="flex items-center gap-2">
              <RadioGroupItem value="all" />
              所有商品
            </Label>
            {categories.map((category) => (
              <Label key={category} className="flex items-center gap-2">
                <RadioGroupItem value={category} />
                {category}
              </Label>
            ))}
          </div>
        </RadioGroup>
      </div>
      
      {/* 價格範圍 */}
      <div>
        <h3 className="font-semibold mb-3">價格範圍</h3>
        <div className="space-y-4">
          <Slider
            min={0}
            max={1000}
            step={10}
            value={[filters.minPrice, filters.maxPrice]}
            onValueChange={([min, max]) => {
              setFilters({ ...filters, minPrice: min, maxPrice: max })
              onFilterChange({ ...filters, minPrice: min, maxPrice: max })
            }}
          />
          <div className="flex justify-between text-sm">
            <span>${filters.minPrice}</span>
            <span>${filters.maxPrice}</span>
          </div>
        </div>
      </div>
      
      {/* 其他篩選選項 */}
      <div>
        <Label className="flex items-center gap-2">
          <Checkbox
            checked={filters.inStock}
            onCheckedChange={(checked) => {
              setFilters({ ...filters, inStock: checked as boolean })
              onFilterChange({ ...filters, inStock: checked as boolean })
            }}
          />
          僅顯示有庫存商品
        </Label>
      </div>
    </aside>
  )
}

最佳實踐與技巧

1. 心理學導向的轉換優化

真正的 One Page Store 不只是技術實作,更是心理學的應用:

稀缺性和緊迫感

// 動態庫存顯示元件
export function InventoryCounter({ initialStock, productName }: InventoryCounterProps) {
  const [stock, setStock] = useState(initialStock)
  const [recentPurchases, setRecentPurchases] = useState(0)
  
  useEffect(() => {
    // 模擬實時購買
    const interval = setInterval(() => {
      if (Math.random() < 0.3) { // 30% 機率有人購買
        setStock(prev => Math.max(0, prev - 1))
        setRecentPurchases(prev => prev + 1)
      }
    }, 30000) // 每 30 秒檢查一次
    
    return () => clearInterval(interval)
  }, [])
  
  return (
    <div className="bg-red-900/20 border border-red-500/50 rounded-lg p-4 text-center">
      <p className="text-red-400 font-bold mb-2">
        ⚠️ 僅剩 {stock} 個!
      </p>
      <p className="text-sm text-gray-300">
        過去 1 小時內已有 {recentPurchases} 人購買 {productName}
      </p>
    </div>
  )
}

社會證明動態顯示

// 實時購買通知
export function LivePurchaseNotifications() {
  const [notifications, setNotifications] = useState<PurchaseNotification[]>([])
  
  const mockPurchases = [
    { name: '李小明', location: '台北', time: '剛剛' },
    { name: '陳美麗', location: '高雄', time: '3 分鐘前' },
    { name: '王大衛', location: '台中', time: '5 分鐘前' }
  ]
  
  useEffect(() => {
    const interval = setInterval(() => {
      const randomNotification = mockPurchases[Math.floor(Math.random() * mockPurchases.length)]
      setNotifications(prev => [randomNotification, ...prev.slice(0, 2)])
    }, 45000) // 每 45 秒顯示一次
    
    return () => clearInterval(interval)
  }, [])
  
  return (
    <div className="fixed bottom-4 left-4 z-50 space-y-2">
      <AnimatePresence>
        {notifications.map((notification, index) => (
          <motion.div
            key={index}
            initial={{ opacity: 0, x: -100 }}
            animate={{ opacity: 1, x: 0 }}
            exit={{ opacity: 0, x: -100 }}
            className="bg-green-600 text-white p-3 rounded-lg shadow-lg max-w-sm"
          >
            <p className="text-sm">
              🎉 {notification.name} 在 {notification.location} {notification.time}購買了 WaveSound Pro
            </p>
          </motion.div>
        ))}
      </AnimatePresence>
    </div>
  )
}

2. 情感連結設計模式

創造情感共鳴的設計元素:

故事化的產品介紹

// 故事化卷軸元件
export function StoryScrollSection() {
  const storySteps = [
    {
      title: "🌃 凌晨 3 點的靈感",
      content: "在這個安靜的深夜,我們的工程師正在細心調整每一個音頻參數...",
      image: "/images/story-1.jpg",
      emotion: "curiosity"
    },
    {
      title: "🎵 第一次轟動",
      content: "當第一首測試音樂播放時,整個實驗室都陷入了青靜...",
      image: "/images/story-2.jpg",
      emotion: "wonder"
    },
    {
      title: "🎆 你的新世界",
      content: "現在,這個技術奇蹟就在你的手中,等待開啟一個全新的音質世界...",
      image: "/images/story-3.jpg",
      emotion: "excitement"
    }
  ]
  
  return (
    <section className="py-20">
      {storySteps.map((step, index) => (
        <motion.div
          key={index}
          initial={{ opacity: 0 }}
          whileInView={{ opacity: 1 }}
          transition={{ duration: 1 }}
          className="min-h-screen flex items-center justify-center relative overflow-hidden"
        >
          <div className="container mx-auto px-4 grid md:grid-cols-2 gap-12 items-center">
            <div className="space-y-6">
              <h2 className="text-4xl font-bold text-white">{step.title}</h2>
              <p className="text-xl text-gray-300 leading-relaxed">{step.content}</p>
            </div>
            <div className="relative">
              <img 
                src={step.image} 
                alt={step.title}
                className="rounded-2xl shadow-2xl transform hover:scale-105 transition-transform duration-700"
              />
            </div>
          </div>
        </motion.div>
      ))}
    </section>
  )
}

感官互動設計

// 觸覺回饋元件
export function HapticFeedbackButton({ children, intensity = 'medium', ...props }: HapticButtonProps) {
  const triggerHaptic = (type: 'light' | 'medium' | 'heavy') => {
    if ('vibrate' in navigator) {
      const patterns = {
        light: [10],
        medium: [20],
        heavy: [30, 10, 30]
      }
      navigator.vibrate(patterns[type])
    }
  }
  
  return (
    <Button
      {...props}
      onMouseDown={() => triggerHaptic(intensity)}
      className={`transform active:scale-95 transition-all duration-150 ${props.className}`}
    >
      {children}
    </Button>
  )
}

// 聲音回饋系統
export function AudioFeedbackProvider({ children }: { children: React.ReactNode }) {
  const playSound = (type: 'click' | 'success' | 'hover') => {
    const audio = new Audio(`/sounds/${type}.mp3`)
    audio.volume = 0.3
    audio.play().catch(() => {}) // 忽略錯誤,有些瀏覽器需要用戶互動
  }
  
  return (
    <div 
      onMouseDown={() => playSound('click')}
      onMouseEnter={() => playSound('hover')}
    >
      {children}
    </div>
  )
}

3. 互動式產品展示

讓用戶「體驗」而非「觀看」產品:

// 360° 產品展示器
export function Product360Viewer({ images }: Product360ViewerProps) {
  const [currentAngle, setCurrentAngle] = useState(0)
  const [isRotating, setIsRotating] = useState(false)
  
  const handleMouseMove = (e: React.MouseEvent) => {
    if (!isRotating) return
    
    const rect = e.currentTarget.getBoundingClientRect()
    const x = e.clientX - rect.left
    const percentage = x / rect.width
    const angle = Math.floor(percentage * 360)
    const imageIndex = Math.floor((angle / 360) * images.length)
    
    setCurrentAngle(imageIndex)
  }
  
  return (
    <div 
      className="relative w-full h-96 cursor-grab active:cursor-grabbing"
      onMouseDown={() => setIsRotating(true)}
      onMouseUp={() => setIsRotating(false)}
      onMouseLeave={() => setIsRotating(false)}
      onMouseMove={handleMouseMove}
    >
      <img 
        src={images[currentAngle]} 
        alt={`產品視角 ${currentAngle}`}
        className="w-full h-full object-contain transition-opacity duration-100"
      />
      
      <div className="absolute bottom-4 left-1/2 transform -translate-x-1/2 bg-black/50 text-white px-3 py-1 rounded-full text-sm">
        🔄 拖曳查看 360° 視角
      </div>
    </div>
  )
}

🏆 One Page Store 最佳實踐指南

1. 設計心理學原則

應用用戶心理學原則提升轉換率:

認知偏差的運用

  • 錯失厭避 (Loss Aversion):強調「節省」而非「優惠」
  • 社會證明 (Social Proof):實時購買通知和評價數量
  • 稀缺性 (Scarcity):限量和倒數計時
  • 錨定效應 (Anchoring):先顯示高價,再顯示優惠價

情感設計模式

// 情感觸發器元件
const emotionalTriggers = {
  curiosity: {
    headline: "你能在 95% 的噪音中聽到什麼?",
    color: "purple",
    animation: "pulse"
  },
  urgency: {
    headline: "僅剩 23 個,錯過就要等下次!",
    color: "red",
    animation: "shake"
  },
  belonging: {
    headline: "加入 12,847 位音樂愛好者的選擇",
    color: "blue",
    animation: "glow"
  }
}

2. 性能與轉換優化

針對 One Page Store 的特殊需求優化:

關鍵指標 (KPIs) 監控

// 轉換漏斗分析
const conversionFunnel = {
  landingView: 100,      // 著陸頁海查看
  featureExplore: 60,    // 功能探索
  interactionDemo: 40,   // 互動測試
  reviewsRead: 30,       // 閱讀評價
  purchaseIntent: 15,    // 點擊購買
  checkoutComplete: 8    // 完成購買
}

// 熱力圖分析
const heatmapTracking = {
  heroSection: 'primary-cta-clicks',
  audioDemo: 'demo-interaction-time',
  features: 'feature-hover-duration',
  reviews: 'review-scroll-depth',
  purchase: 'purchase-button-attention'
}

3. A/B 測試策略

持續優化轉換率的測試方法:

關鍵元素測試

// A/B 測試配置
const abTestVariants = {
  heroHeadline: {
    A: "重新定義你的聆聽體驗",
    B: "一次性解決所有噪音問題",
    C: "為什麼 12,847 人選擇 WaveSound Pro?"
  },
  primaryCTA: {
    A: "立即購買",
    B: "開始你的完美音質之旅",
    C: "只要 $249,改變你的世界"
  },
  socialProof: {
    A: "已有 12,847 人購買",
    B: "4.9/5 星級評價,2,847 則評論",
    C: "現在就有 234 人正在瀏覽這個頁面"
  }
}

// 動態分流系統
export function useABTest(testName: string) {
  const [variant, setVariant] = useState<string>('A')
  
  useEffect(() => {
    // 根據用戶 ID 或隨機分配變體
    const userVariant = getUserVariant(testName)
    setVariant(userVariant)
    
    // 記錄分流事件
    trackEvent('ab_test_assignment', {
      testName,
      variant: userVariant,
      timestamp: Date.now()
    })
  }, [testName])
  
  return variant
}

4. 無障礙與包容性設計

確保所有用戶都能享受完美體驗:

多媒體內容的替代方案

// 觸覺回饋的替代方案
export function AccessibleAudioDemo({ tracks }: AudioDemoProps) {
  const [transcript, setTranscript] = useState('')
  const [isHighContrast, setIsHighContrast] = useState(false)
  
  return (
    <div className={`audio-demo ${isHighContrast ? 'high-contrast' : ''}`}>
      {/* 視覺指示器取代音頻 */}
      <div className="visual-indicator" aria-live="polite">
        <div className="volume-bars">
          {[...Array(10)].map((_, i) => (
            <div 
              key={i} 
              className={`bar ${isPlaying && i < volume * 10 ? 'active' : ''}`}
              style={{ height: `${(i + 1) * 10}%` }}
            />
          ))}
        </div>
      </div>
      
      {/* 字幕和譯本 */}
      <div className="transcript-section">
        <h3>音頻內容描述</h3>
        <p>{transcript}</p>
      </div>
      
      {/* 高對比度模式 */}
      <button 
        onClick={() => setIsHighContrast(!isHighContrast)}
        aria-label="切換高對比度模式"
      >
        {isHighContrast ? '關閉' : '開啟'}高對比度
      </button>
    </div>
  )
}

// 焦點管理
export function FocusManager({ children }: { children: React.ReactNode }) {
  useEffect(() => {
    const handleKeyDown = (e: KeyboardEvent) => {
      // Tab 鍵管理
      if (e.key === 'Tab') {
        const focusableElements = document.querySelectorAll(
          'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
        )
        // 實作循環焦點邏輯
      }
    }
    
    document.addEventListener('keydown', handleKeyDown)
    return () => document.removeEventListener('keydown', handleKeyDown)
  }, [])
  
  return <div role="main">{children}</div>
}

🚀 部署與性能優化

Vercel 部署:專為 One Page Store 優化

Vercel 是部署 Next.js 應用程式的最佳選擇,特別適合 One Page Store 的需求:

  1. 全球 CDN 加速:確保全球用戶都能快速訪問
  2. 自動圖片優化:對於大量產品圖片至關重要
  3. 即時部署:快速更新優惠和產品資訊
  4. A/B 測試支持:優化轉換率

性能監控與轉換率優化

對於 One Page Store,關鍵指標不只是技術性能:

  • Core Web Vitals:影響 SEO 和使用者體驗
  • 互動元素效能:音頻播放、動畫效果的流暢度
  • 轉換漏斗分析:從訪問到購買的每個步驟
  • 用戶行為熱力圖:了解用戶在頁面上的互動節點

🎆 結論:你已經掌握了真正的 One Page Store 精髓

透過這篇文章,我們不僅學會了如何使用 Next.js、React、MDX、Tailwind CSS、Shadcn/UI 和 TypeScript 建立技術上先進的網站,更重要的是,我們重新定義了什麼才是真正的 One Page Store

🎨 從技術實作到藝術創造

不同於傳統的電商網站,One Page Store 是一個整合的藝術作品

  • 單一產品聚焦 → 像 Whirly Birdie 專注於字體的美學展示
  • 沉浸式體驗 → 像 USB Club Transport 創造的視覺震撼
  • 情感連結 → 從理性認知轉化為情感渴望
  • 完整故事 → 每個元素都為購買轉換服務

💪 技術堆棧的最佳實踐

我們選擇的這套技術堆棧完美平衡了:

  1. 開發效率 🚀 - Shadcn/UI 和 Tailwind 讓設計實作飛快
  2. 效能優異 ⚡ - Next.js 確保每個互動都流暢無礙
  3. 型別安全 🔒 - TypeScript 避免生產環境錯誤
  4. 內容管理 📝 - MDX 讓產品故事生動有趣
  5. 擴展性強 🌱 - 模組化設計支持未來成長

🎯 定制化建議

根據你的產品類型,可以進一步優化:

技術產品

  • 加強互動展示(像我們的耳機音質對比)
  • 詳細的技術規格和測試數據

生活用品

  • 著重生活場景展示
  • 真實用戶使用影片

服務產品

  • 案例研究和成果展示
  • 客戶證言影片

🚀 下一步行動指南

  1. 立即開始:使用我們提供的代碼模版建立你的第一個 One Page Store
  2. 分析競品:研究 Whirly Birdie、USB Club Transport 等成功案例
  3. 測試優化:持續 A/B 測試不同的設計元素
  4. 收集回饋:定期分析用戶行為和轉換數據

🌟 最終思考

在 2025 年,成功的電商不再只是關於「賣東西」,而是關於「創造體驗」。One Page Store 代表了電商的未來:

  • 品牌故事化 → 每個產品都是一個宇宙
  • 技術藝術化 → 代碼成為創意表達的工具
  • 互動體驗化 → 用戶不再是觀眾,而是參與者

當你掌握了這些技能和理念,你就不只是在建立一個網站,而是在創造一個數位體驗藝術作品。這就是真正的 One Page Store 的魅力所在 —— 它讓每一次訪問都成為一次難忘的旅程。

現在,開始創造你的數位傳奇吧! 🚀✨

Thanks for reading!

Found this article helpful? Share it with others or explore more content.

More Articles
Published June 8, 202552 min read8 tags