从零到一:Nuxt3 + Vue3 + TS + Vite + Ant Design Vue 全栈项目搭建实战
现代前端开发已经进入模块化、组件化的时代,而Nuxt3作为Vue生态中最受欢迎的SSR框架之一,结合Vue3的Composition API和TypeScript的强类型支持,能够为开发者提供极致的开发体验。本文将带你从零开始,一步步搭建一个完整的全栈项目,涵盖从项目初始化到生产环境部署的全流程。
1. 环境准备与项目初始化
在开始之前,确保你的开发环境满足以下要求:
- Node.js版本 ≥16.10.0(推荐使用最新的LTS版本)
- npm ≥7.0.0 或 yarn ≥1.22.0
- 代码编辑器(推荐VS Code)
创建Nuxt3项目非常简单,只需运行以下命令:
npx nuxi init my-nuxt3-project cd my-nuxt3-project npm install这个命令会创建一个基本的Nuxt3项目结构。让我们看看生成的核心目录:
├── assets/ # 静态资源 ├── components/ # Vue组件 ├── composables/ # 可复用逻辑 ├── layouts/ # 布局文件 ├── pages/ # 页面路由 ├── plugins/ # 插件 ├── public/ # 静态文件 ├── server/ # 服务器API ├── nuxt.config.ts # 配置文件 └── tsconfig.json # TypeScript配置提示:Nuxt3默认使用Vite作为构建工具,相比Webpack有更快的启动和热更新速度。
2. 核心依赖配置与优化
2.1 集成Ant Design Vue
Ant Design Vue是一套企业级UI组件库,安装最新版本:
npm install ant-design-vue@4.x在plugins/antd.ts中配置全局引入:
import Antd from 'ant-design-vue' export default defineNuxtPlugin((nuxtApp) => { nuxtApp.vueApp.use(Antd) })按需加载可以减小打包体积,修改配置如下:
import { Button, Layout, Menu } from 'ant-design-vue' export default defineNuxtPlugin((nuxtApp) => { nuxtApp.vueApp.use(Button).use(Layout).use(Menu) })2.2 状态管理:Pinia与持久化
Pinia是Vue官方推荐的状态管理库,比Vuex更轻量且支持TypeScript:
npm install pinia @pinia/nuxt pinia-plugin-persistedstate在nuxt.config.ts中配置:
export default defineNuxtConfig({ modules: [ '@pinia/nuxt', '@pinia-plugin-persistedstate/nuxt' ] })创建用户状态存储stores/user.ts:
import { defineStore } from 'pinia' export const useUserStore = defineStore('user', { state: () => ({ token: '', userInfo: null }), persist: { storage: process.client ? localStorage : null } })3. 主题定制与样式管理
Ant Design Vue支持动态主题定制,在布局文件中配置:
<template> <a-config-provider :theme="{ token: { colorPrimary: '#1890ff', borderRadius: 4, } }"> <NuxtPage /> </a-config-provider> </template>样式管理最佳实践:
- 全局样式放在
assets/styles目录 - 组件样式使用scoped CSS
- 主题变量通过CSS变量管理
创建全局样式文件assets/styles/main.scss:
:root { --primary-color: #1890ff; --secondary-color: #52c41a; } body { font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif; }4. 开发环境优化与生产部署
4.1 开发环境配置
在nuxt.config.ts中添加开发工具:
export default defineNuxtConfig({ devtools: { enabled: true }, vite: { server: { proxy: { '/api': { target: 'http://localhost:3000', changeOrigin: true } } } } })4.2 生产环境部署
使用PM2进行Node.js应用管理:
npm install pm2 -g创建ecosystem.config.js:
module.exports = { apps: [{ name: 'nuxt-app', script: 'node .output/server/index.mjs', env: { NITRO_PORT: 3000, NODE_ENV: 'production' } }] }启动命令:
npm run build pm2 start ecosystem.config.js部署优化建议:
- 使用Nginx作为反向代理
- 配置HTTPS证书
- 设置合理的缓存策略
- 启用Gzip压缩
5. 常见问题与解决方案
在实际开发中,你可能会遇到以下问题:
- HMR不工作:检查Vite配置,确保
server.hmr设置正确 - TypeScript类型错误:安装对应的类型声明文件
- 样式冲突:使用CSS Modules或scoped样式
- 构建体积过大:分析依赖并优化
性能优化技巧:
- 使用
<script setup>语法 - 合理使用Composition API的
ref和reactive - 按需引入第三方库
- 使用
v-memo优化渲染性能
6. 项目结构与代码组织
一个良好的项目结构能显著提高开发效率。推荐如下组织方式:
├── components/ │ ├── common/ # 通用组件 │ ├── layout/ # 布局组件 │ └── ui/ # UI基础组件 ├── composables/ │ ├── useAuth.ts # 认证逻辑 │ └── useApi.ts # API封装 ├── pages/ │ ├── [id].vue # 动态路由 │ └── index.vue ├── server/ │ ├── api/ # API路由 │ └── middleware/ # 服务端中间件 └── utils/ # 工具函数代码规范建议:
- 使用ESLint + Prettier统一代码风格
- 提交前运行类型检查和测试
- 遵循单一职责原则组织组件
- 合理拆分大型组件
7. 测试与质量保障
完善的测试是项目质量的保证。配置测试环境:
npm install vitest @vue/test-utils happy-dom -D创建简单的组件测试tests/Button.spec.ts:
import { mount } from '@vue/test-utils' import MyButton from '../components/MyButton.vue' test('renders button', () => { const wrapper = mount(MyButton, { props: { text: 'Click me' } }) expect(wrapper.text()).toContain('Click me') })测试策略:
| 测试类型 | 工具 | 覆盖范围 |
|---|---|---|
| 单元测试 | Vitest | 组件/工具函数 |
| 组件测试 | Testing Library | UI交互 |
| E2E测试 | Cypress | 完整用户流程 |
在开发过程中,我发现合理使用Composition API封装业务逻辑能显著提高代码复用性。例如,将数据获取逻辑封装成可组合函数:
// composables/useFetchData.ts export const useFetchData = (url: string) => { const data = ref(null) const error = ref(null) const loading = ref(false) const fetchData = async () => { loading.value = true try { const response = await fetch(url) data.value = await response.json() } catch (err) { error.value = err } finally { loading.value = false } } onMounted(fetchData) return { data, error, loading, fetchData } }