Nuxt.js核心功能项目实战
学习目标
通过本章节的学习,你将能够:
- 掌握Nuxt.js项目的需求分析方法
- 了解技术方案设计的流程和要点
- 熟练应用数据获取和状态管理技术
- 掌握路由和导航的高级配置
- 了解错误处理和国际化的实现方式
- 掌握性能优化和部署的实践技巧
核心知识点
项目需求分析
在开始项目实战之前,我们需要先进行需求分析,明确项目的功能和目标。
项目背景
我们将创建一个电商网站,包含以下功能:
- 商品展示:展示商品列表和详情
- 用户系统:注册、登录、个人中心
- 购物车:添加商品、修改数量、删除商品
- 订单系统:创建订单、支付、订单管理
- 搜索功能:根据关键词搜索商品
- 分类浏览:根据分类浏览商品
技术栈选择
- 前端框架:Nuxt.js 3
- 状态管理:Pinia
- HTTP客户端:Axios
- UI组件库:Element Plus
- CSS预处理器:SCSS
- 图标库:Font Awesome
- 支付集成:Stripe
技术方案设计
项目结构设计
nuxt-ecommerce/
├── assets/ # 静态资源
│ ├── css/ # 样式文件
│ ├── images/ # 图片资源
│ └── fonts/ # 字体文件
├── components/ # 组件
│ ├── common/ # 通用组件
│ ├── layout/ # 布局组件
│ ├── product/ # 商品相关组件
│ ├── user/ # 用户相关组件
│ └── cart/ # 购物车相关组件
├── composables/ # 组合式API
├── pages/ # 页面
│ ├── index.vue # 首页
│ ├── products/ # 商品相关页面
│ │ ├── index.vue # 商品列表
│ │ └── _id.vue # 商品详情
│ ├── categories/ # 分类相关页面
│ │ └── _id.vue # 分类商品列表
│ ├── cart.vue # 购物车页面
│ ├── checkout.vue # 结算页面
│ ├── orders/ # 订单相关页面
│ │ ├── index.vue # 订单列表
│ │ └── _id.vue # 订单详情
│ └── user/ # 用户相关页面
│ ├── login.vue # 登录页面
│ ├── register.vue # 注册页面
│ └── profile.vue # 个人中心
├── plugins/ # 插件
├── stores/ # 状态管理
│ ├── product.js # 商品状态
│ ├── cart.js # 购物车状态
│ ├── user.js # 用户状态
│ └── order.js # 订单状态
├── server/ # 服务器端
│ ├── api/ # API路由
│ └── middleware/ # 服务器中间件
├── static/ # 静态文件
├── utils/ # 工具函数
├── nuxt.config.js # 配置文件
├── package.json # 项目依赖
└── README.md # 项目说明数据结构设计
- 商品数据结构
{
id: Number, // 商品ID
name: String, // 商品名称
description: String, // 商品描述
price: Number, // 商品价格
originalPrice: Number, // 原价
discount: Number, // 折扣
stock: Number, // 库存
categoryId: Number, // 分类ID
images: Array, // 商品图片
attributes: Object, // 商品属性
createdAt: Date, // 创建时间
updatedAt: Date // 更新时间
}- 用户数据结构
{
id: Number, // 用户ID
username: String, // 用户名
email: String, // 邮箱
password: String, // 密码(加密存储)
firstName: String, // 名字
lastName: String, // 姓氏
address: Object, // 地址
phone: String, // 电话
createdAt: Date, // 创建时间
updatedAt: Date // 更新时间
}- 购物车数据结构
{
id: Number, // 购物车项ID
userId: Number, // 用户ID
productId: Number, // 商品ID
quantity: Number, // 数量
product: Object, // 商品信息
createdAt: Date, // 创建时间
updatedAt: Date // 更新时间
}- 订单数据结构
{
id: Number, // 订单ID
userId: Number, // 用户ID
items: Array, // 订单商品
totalPrice: Number, // 总价格
status: String, // 订单状态
paymentMethod: String, // 支付方式
shippingAddress: Object, // 收货地址
createdAt: Date, // 创建时间
updatedAt: Date // 更新时间
}数据获取和状态管理
数据获取
使用Nuxt.js的useAsyncData和useFetch组合式API进行数据获取:
// pages/products/index.vue
<script setup>
const { data: products, pending, error } = useAsyncData('products', () => {
return $fetch('/api/products')
})
</script>状态管理
使用Pinia进行状态管理:
// stores/product.js
import { defineStore } from 'pinia'
export const useProductStore = defineStore('product', {
state: () => ({
products: [],
currentProduct: null,
loading: false,
error: null
}),
actions: {
async fetchProducts() {
this.loading = true
try {
this.products = await $fetch('/api/products')
this.error = null
} catch (err) {
this.error = err
} finally {
this.loading = false
}
},
async fetchProduct(id) {
this.loading = true
try {
this.currentProduct = await $fetch(`/api/products/${id}`)
this.error = null
} catch (err) {
this.error = err
} finally {
this.loading = false
}
}
}
})路由和导航
路由配置
使用Nuxt.js的基于文件系统的路由:
- 商品列表页面:
pages/products/index.vue - 商品详情页面:
pages/products/_id.vue - 分类商品页面:
pages/categories/_id.vue - 购物车页面:
pages/cart.vue - 结算页面:
pages/checkout.vue - 订单列表页面:
pages/orders/index.vue - 订单详情页面:
pages/orders/_id.vue - 登录页面:
pages/user/login.vue - 注册页面:
pages/user/register.vue - 个人中心页面:
pages/user/profile.vue
导航守卫
使用Nuxt.js的中间件实现导航守卫:
// middleware/auth.js
export default defineNuxtRouteMiddleware((to, from) => {
const userStore = useUserStore()
if (!userStore.isAuthenticated) {
return navigateTo('/user/login')
}
})在需要认证的页面中使用:
<!-- pages/user/profile.vue -->
<template>
<div class="profile">
<!-- 个人中心内容 -->
</div>
</template>
<script setup>
// 使用认证中间件
definePageMeta({
middleware: 'auth'
})
</script>错误处理和国际化
错误处理
- 全局错误处理
在plugins/error-handler.js中配置全局错误处理:
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.hook('vue:error', (err, instance, info) => {
console.error('Vue error:', err)
// 可以在这里添加错误监控逻辑
})
process.on('unhandledRejection', (err) => {
console.error('Unhandled rejection:', err)
})
process.on('uncaughtException', (err) => {
console.error('Uncaught exception:', err)
})
})- 页面级错误处理
使用Nuxt.js的错误边界:
<!-- pages/_error.vue -->
<template>
<div class="error-page">
<h1 v-if="error.statusCode === 404">页面不存在</h1>
<h1 v-else>服务器错误</h1>
<p>{{ error.message }}</p>
<NuxtLink to="/">返回首页</NuxtLink>
</div>
</template>
<script setup>
defineProps({
error: {
type: Object,
required: true
}
})
</script>国际化
使用Nuxt.js的i18n模块实现国际化:
- 安装依赖
npm install @nuxtjs/i18n- 配置i18n
在nuxt.config.js中添加:
export default {
modules: [
'@nuxtjs/i18n'
],
i18n: {
locales: [
{
code: 'en',
name: 'English',
file: 'en.json'
},
{
code: 'zh',
name: '中文',
file: 'zh.json'
}
],
defaultLocale: 'en',
lazy: true,
langDir: 'locales/'
}
}- 创建语言文件
创建locales/en.json和locales/zh.json文件:
// locales/en.json
{
"welcome": "Welcome to our store",
"products": "Products",
"cart": "Cart",
"login": "Login",
"register": "Register"
}// locales/zh.json
{
"welcome": "欢迎来到我们的商店",
"products": "商品",
"cart": "购物车",
"login": "登录",
"register": "注册"
}- 使用国际化
在组件中使用:
<template>
<div>
<h1>{{ $t('welcome') }}</h1>
<NuxtLink to="/products">{{ $t('products') }}</NuxtLink>
</div>
</template>性能优化和部署
性能优化
- 代码分割
使用Nuxt.js的自动代码分割功能:
// nuxt.config.js
export default {
build: {
splitChunks: {
layouts: true,
pages: true,
commons: true
}
}
}- 图片优化
使用Nuxt.js的图片组件:
<template>
<NuxtImg src="/products/1.jpg" alt="Product image" width="300" height="300" />
</template>- 缓存策略
配置HTTP缓存:
// server/middleware/cache.js
export default defineEventHandler((event) => {
// 设置静态资源缓存
if (event.node.req.url.startsWith('/_nuxt/')) {
event.node.res.setHeader('Cache-Control', 'max-age=31536000, immutable')
}
})部署
- 静态生成部署
npm run generate- 服务端渲染部署
npm run build
npm run start- 容器化部署
创建Dockerfile:
FROM node:16-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
EXPOSE 3000
CMD ["npm", "run", "start"]实用案例分析
商品详情页面实现
功能需求
- 展示商品基本信息
- 展示商品图片
- 展示商品属性和价格
- 添加到购物车功能
- 相关商品推荐
实现步骤
- 创建商品详情页面
<!-- pages/products/_id.vue -->
<template>
<div class="product-detail">
<div v-if="pending">加载中...</div>
<div v-else-if="error">{{ error.message }}</div>
<div v-else-if="product">
<div class="product-images">
<NuxtImg
v-for="(image, index) in product.images"
:key="index"
:src="image"
:alt="product.name"
class="product-image"
/>
</div>
<div class="product-info">
<h1>{{ product.name }}</h1>
<div class="product-price">
<span class="current-price">{{ product.price }}</span>
<span v-if="product.originalPrice" class="original-price">{{ product.originalPrice }}</span>
</div>
<div class="product-description">
<p>{{ product.description }}</p>
</div>
<div class="product-attributes">
<!-- 商品属性 -->
</div>
<div class="product-actions">
<button
class="add-to-cart"
@click="addToCart(product)"
>
添加到购物车
</button>
</div>
</div>
<div class="related-products">
<h2>相关商品</h2>
<!-- 相关商品列表 -->
</div>
</div>
</div>
</template>
<script setup>
const route = useRoute()
const productStore = useProductStore()
const cartStore = useCartStore()
const { data: product, pending, error } = useAsyncData('product', () => {
return $fetch(`/api/products/${route.params.id}`)
})
const addToCart = (product) => {
cartStore.addToCart(product)
}
</script>- 实现添加到购物车功能
// stores/cart.js
import { defineStore } from 'pinia'
export const useCartStore = defineStore('cart', {
state: () => ({
items: [],
loading: false,
error: null
}),
getters: {
totalItems: (state) => {
return state.items.reduce((total, item) => total + item.quantity, 0)
},
totalPrice: (state) => {
return state.items.reduce((total, item) => {
return total + (item.product.price * item.quantity)
}, 0)
}
},
actions: {
addToCart(product) {
const existingItem = this.items.find(item => item.product.id === product.id)
if (existingItem) {
existingItem.quantity++
} else {
this.items.push({
product,
quantity: 1
})
}
// 保存到本地存储
localStorage.setItem('cart', JSON.stringify(this.items))
},
removeFromCart(productId) {
this.items = this.items.filter(item => item.product.id !== productId)
localStorage.setItem('cart', JSON.stringify(this.items))
},
updateQuantity(productId, quantity) {
const item = this.items.find(item => item.product.id === productId)
if (item) {
item.quantity = quantity
localStorage.setItem('cart', JSON.stringify(this.items))
}
},
clearCart() {
this.items = []
localStorage.removeItem('cart')
},
loadCart() {
const savedCart = localStorage.getItem('cart')
if (savedCart) {
this.items = JSON.parse(savedCart)
}
}
}
})购物车页面实现
功能需求
- 展示购物车商品列表
- 修改商品数量
- 删除购物车商品
- 显示购物车总价
- 跳转到结算页面
实现步骤
- 创建购物车页面
<!-- pages/cart.vue -->
<template>
<div class="cart">
<h1>购物车</h1>
<div v-if="cartStore.items.length === 0" class="empty-cart">
<p>购物车为空</p>
<NuxtLink to="/products">去购物</NuxtLink>
</div>
<div v-else>
<div class="cart-items">
<div v-for="item in cartStore.items" :key="item.product.id" class="cart-item">
<div class="item-image">
<NuxtImg :src="item.product.images[0]" :alt="item.product.name" />
</div>
<div class="item-info">
<h3>{{ item.product.name }}</h3>
<p class="item-price">{{ item.product.price }}</p>
</div>
<div class="item-quantity">
<button @click="cartStore.updateQuantity(item.product.id, item.quantity - 1)" :disabled="item.quantity <= 1">-</button>
<span>{{ item.quantity }}</span>
<button @click="cartStore.updateQuantity(item.product.id, item.quantity + 1)">+</button>
</div>
<div class="item-total">
{{ item.product.price * item.quantity }}
</div>
<div class="item-remove">
<button @click="cartStore.removeFromCart(item.product.id)">删除</button>
</div>
</div>
</div>
<div class="cart-summary">
<h2>购物车总计</h2>
<div class="summary-item">
<span>商品数量</span>
<span>{{ cartStore.totalItems }}</span>
</div>
<div class="summary-item">
<span>商品总价</span>
<span>{{ cartStore.totalPrice }}</span>
</div>
<div class="summary-item total">
<span>总计</span>
<span>{{ cartStore.totalPrice }}</span>
</div>
<NuxtLink to="/checkout" class="checkout-btn">去结算</NuxtLink>
</div>
</div>
</div>
</template>
<script setup>
const cartStore = useCartStore()
// 加载购物车数据
onMounted(() => {
cartStore.loadCart()
})
</script>总结
本章节通过一个电商网站项目实战,巩固了Nuxt.js的核心功能知识点:
- 项目需求分析:明确项目功能和目标,选择合适的技术栈。
- 技术方案设计:设计项目结构和数据结构,为开发做好准备。
- 数据获取和状态管理:使用
useAsyncData和useFetch进行数据获取,使用Pinia进行状态管理。 - 路由和导航:使用Nuxt.js的基于文件系统的路由,实现导航守卫。
- 错误处理和国际化:实现全局和页面级错误处理,配置国际化支持。
- 性能优化和部署:实现代码分割、图片优化和缓存策略,配置不同的部署方式。
通过本章节的学习,你应该能够独立开发一个功能完整的Nuxt.js应用,并掌握项目开发的流程和最佳实践。