404 Not Found

404 Not Found


nginx

Comprehensive Exercises — Getting Started

Now that we've completed the five lessons in Phase 1, it's time to tie everything together. Charlie has asked the team to build a working basic version of MegaShop—including a home page, category pages, and product detail pages, as well as SSR pre-fetching and proper layout switching. Alice will act as a customer to test the user experience.

1. What You'll Learn


2. A True Story About a Team

(1) Pain Point: Inability to Synthesize Disparate Knowledge

Alice has learned about routing, components, and data fetching, but doesn't know how to put them together into a complete project. The page Bob wrote has no layout, and the page flickers after the product data loads. Charlie needs an MVP to demonstrate to investors.

(2) Comprehensive Solutions for Real-World Problems

Integrate all the concepts from Phase 1 into a single project: file routing + layout system + useFetch SSR prefetching + automatic component import, and build a working e-commerce platform from scratch.

(3) Benefits: From Parts to Products

By the end of this chapter, Alice will have a working basic version of MegaShop—with four pages, two layouts, server-side rendering (SSR) with pre-fetched product data, and SEO-friendly HTML output.


3. Project Planning

(1) MegaShop Basic Edition Architecture

100%
flowchart TB
    A[MegaShop Phase 1] --> B[Pages]
    A --> C[Layouts]
    A --> D[Components]
    A --> E[API Routes]

    B --> B1[/ → Homepage]
    B --> B2[/products → Product List]
    B --> B3[/products/[id] → Product Detail]
    B --> B4[/about → About Page]

    C --> C1[default → Header + Footer]
    C --> C2[sidebar → Admin Sidebar]

    D --> D1[AppHeader]
    D --> D2[AppFooter]
    D --> D3[ProductCard]

    E --> E1[GET /api/products]
    E --> E2[GET /api/products/:id]

(2) Page and Routing Design

URL Page File Layout Features
/ pages/index.vue default Home: Featured Products
/products pages/products/index.vue default Product List (SSR Prefetch)
/products/[id] pages/products/[id].vue default Product Details (SSR Prefetch)
/about pages/about.vue default About Page
/admin pages/admin/index.vue sidebar Admin Dashboard
404 Page Not Found default Custom 404

4. Building the Page Structure

(1) ▶ Example: app.vue root component

VUE
<!-- app.vue -->
<template>
  <NuxtLayout>
    <NuxtPage />
  </NuxtLayout>
</template>

Output:

TEXT
// Execution Successful

(2) ▶ Example: default layout

VUE
<!-- layouts/default.vue -->
<template>
  <div class="layout-default">
    <AppHeader />
    <main class="container">
      <slot />
    </main>
    <AppFooter />
  </div>
</template>

Output:

TEXT
// Execution Successful

(3) ▶ Example: Sidebar Layout

VUE
<!-- layouts/sidebar.vue -->
<template>
  <div class="layout-sidebar">
    <AppHeader />
    <div class="main-wrapper">
      <aside class="sidebar">
        <h3>Admin Panel</h3>
        <nav>
          <NuxtLink to="/admin">Dashboard</NuxtLink>
          <NuxtLink to="/admin/products">Products</NuxtLink>
          <NuxtLink to="/admin/orders">Orders</NuxtLink>
        </nav>
      </aside>
      <main class="content">
        <slot />
      </main>
    </div>
  </div>
</template>

Output:

TEXT
// Execution Successful

5. Core API Routing

(1) ▶ Example: Product List API

TYPESCRIPT
// server/api/products/index.get.ts
interface Product {
  id: number; name: string; price: number
  image: string; category: string; inStock: boolean
}

const mockProducts: Product[] = Array.from({ length: 100 }, (_, i) => ({
  id: i + 1,
  name: `Product ${i + 1}`,
  price: Math.round(Math.random() * 500 * 100) / 100,
  image: `/images/product-${i + 1}.webp`,
  category: ['electronics', 'clothing', 'home'][i % 3],
  inStock: Math.random() > 0.2
}))

export default defineEventHandler((event) => {
  const query = getQuery(event)
  const page = Number(query.page) || 1
  const limit = Number(query.limit) || 12
  const category = query.category as string

  let filtered = mockProducts
  if (category) filtered = filtered.filter(p => p.category === category)

  const start = (page - 1) * limit
  return {
    items: filtered.slice(start, start + limit),
    total: filtered.length,
    page,
    limit
  }
})

Output:

TEXT
// Execution Successful

(2) ▶ Example: Product Details API

TYPESCRIPT
// server/api/products/[id].get.ts
export default defineEventHandler((event) => {
  const id = Number(getRouterParam(event, 'id'))
  const product = {
    id,
    name: `Premium Product ${id}`,
    price: 299.99,
    image: `/images/product-${id}.webp`,
    category: 'electronics',
    inStock: true,
    description: `High-quality product #${id} with premium features.`,
    reviewCount: Math.floor(Math.random() * 5 * 1000),
    rating: 4.5
  }

  if (id > 100) {
    throw createError({ statusCode: 404, message: 'Product not found' })
  }

  return product
})

Output:

TEXT
// Execution Successful

6. Page Implementation

(1) ▶ Example: SSR Prefetching for the Home Page

VUE
<!-- pages/index.vue -->
<template>
  <div class="homepage">
    <section class="hero">
      <h1>MegaShop</h1>
      <p>Over 1 million products, shipped worldwide</p>
      <NuxtLink to="/products" class="cta">Shop Now</NuxtLink>
    </section>
    <section class="featured">
      <h2>Featured Products</h2>
      <div class="grid">
        <ProductCard v-for="p in featured" :key="p.id" :product="p" />
      </div>
    </section>
  </div>
</template>

<script setup lang="ts">
const { data: featured } = await useFetch('/api/products', {
  query: { limit: 6 },
  transform: (data: any) => data.items
})

useHead({ title: 'MegaShop - Premium E-Commerce' })
</script>

Output:

TEXT
// Execution Successful

(2) ▶ Example: Product Details Page

VUE
<!-- pages/products/[id].vue -->
<template>
  <div v-if="product" class="product-detail">
    <img :src="product.image" :alt="product.name" />
    <div class="info">
      <h1>{{ product.name }}</h1>
      <p class="price">${{ product.price }} USD</p>
      <p>{{ product.description }}</p>
      <p>{{ product.reviewCount }} reviews · {{ product.rating }} stars</p>
      <button @click="addToCart">Add to Cart</button>
    </div>
  </div>
  <div v-else class="not-found">
    <h1>Product Not Found</h1>
    <NuxtLink to="/products">Back to Products</NuxtLink>
  </div>
</template>

<script setup lang="ts">
const route = useRoute()
const { data: product } = await useFetch(`/api/products/${route.params.id}`)

useHead({
  title: computed(() => product.value ? `${product.value.name} - MegaShop` : 'Not Found')
})

const cart = useState<any[]>('cart', () => [])
function addToCart() {
  if (product.value) cart.value.push(product.value)
}
</script>

Output:

TEXT
// Execution Successful

7. Code Review Checklist

(1) SSR Data Hydration Check

Check Item Correct Approach Common Mistakes
Using useFetch await useFetch() Waiting for data Forgetting to use await results in a pending state
SSR View Source Code HTML Contains Product Data Only Empty <div>
Payload Passed Client Does Not Repeat Requests Duplicate API Requests in Network
route.params Retrieved at the top level of setup Retrieved in onMounted (this hook is not available in SSR)

(2) Auto-import Validity Check

Check Item Correct Approach Common Mistakes
Component References <ProductCard /> Auto-import Manually import components
Composable useFetch() Use directly Manually import useFetch
useState useState('key') Use directly Import manually from 'nuxt/app'
Component Name Reference Based on Directory Prefix Rules Component Name Does Not Match File Path

8. Comprehensive Example: MegaShop Basic Edition nuxt.config.ts

TYPESCRIPT
// nuxt.config.ts
export default defineNuxtConfig({
  ssr: true,

  app: {
    head: {
      title: 'MegaShop',
      meta: [
        { name: 'description', content: 'Premium e-commerce with over 1 million products' }
      ]
    }
  },

  modules: ['@nuxtjs/tailwindcss', '@pinia/nuxt'],

  components: [{ path: '~/components', pathPrefix: false }],

  routeRules: {
    '/': { prerender: true },
    '/products': { swr: 60 },
    '/products/**': { swr: 3600 },
    '/admin/**': { ssr: false }
  }
})

❓ FAQ

Q Why doesn't the data update after the homepage is prerendered?
A Setting prerender: true generates static HTML during the build process, so the data will not update automatically. You'll need to rebuild or switch to the swr strategy.
Q Why does the product detail page sometimes use SSR and sometimes not?
A Check whether useFetch is correctly used with await. If useFetch is inside a conditional statement, SSR may skip it. Make sure to call it at the top level of setup.
Q How is a 404 page triggered?
A Nuxt 3 automatically displays a 404 page when a route cannot be matched. You can also throw a createError({ statusCode: 404 }) at the API level.
Q What should I do if there's flickering when switching layouts?
A Layouts are re-rendered when switching; you can wrap the slot in <Transition> to add a transition animation, or use the pageTransition configuration.
Q How will mock data be replaced in the production environment?
A In Phase 2, Prisma will replace the mocks. The current API response format will remain consistent; later on, we'll simply need to modify the server API implementation.
Q How can I verify if SSR is working?
A Use your browser's "View Source" function (Ctrl+U). If the HTML includes product names and prices, SSR is working. If there is only an empty div, it is CSR.

📖 Summary


📝 Exercises

  1. Basic Exercise (Difficulty: ⭐): Complete all pages of the MegaShop Basic Edition and ensure that all routes are accessible.
  2. Advanced Exercise (Difficulty: ⭐⭐): Add a product category filter feature, and use watch and useFetch on the product list page to automatically refresh the page when switching categories.
  3. Challenge (Difficulty: ⭐⭐⭐): Add an admin/products page (sidebar layout + CSR mode), implement a product table display, and compare the differences between CSR and SSR pages when viewing the source code.

---|

Web-Tutorial.com

Web-Tutorial Tech Team

A team of developers maintaining programming tutorials. Each tutorial is written and reviewed by developers with expertise in that field. We work to keep our content accurate and reliable — if you spot an issue, please let us know.

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏