🚀 全新升级 - Nuxt4 AI企业级开发实战指南现已发布!

目录结构与配置详解

理解Nuxt.js的目录结构与约定规范,掌握企业级应用的项目组织最佳实践

引言

Nuxt 采用约定胜于配置的设计理念,通过 pages/ 目录结构自动生成路由配置。这种方式极大简化了路由管理,同时提供了强大的动态路由能力。

核心目录

pages 目录与路由

pages 目录是Nuxt应用的核心,负责自动生成路由系统,所有放在此目录下的 Vue 文件都会自动生成对应的路由。

基本结构

pages/
├── index.vue          # / 路由
├── about.vue          # /about 路由
├── user/
│   ├── index.vue      # /user 路由
│   ├── profile.vue    # /user/profile 路由
│   └── [id].vue       # /user/:id 动态路由
└── blog/
    ├── index.vue      # /blog 路由
    ├── [slug].vue     # /blog/:slug 动态路由
    └── [...slug].vue  # /blog/* 捕获所有路由

动态路由

创建动态路由页面:

<!-- pages/users/[id].vue -->
<template>
  <div>
    <h1>用户详情页</h1>
    <p>用户ID: {{ $route.params.id }}</p>
  </div>
</template>

<script setup lang="ts">
// 获取路由参数
const route = useRoute()
const userId = route.params.id

// 类型安全的路由参数
definePageMeta({
  validate: (route) => {
    return /^\d+$/.test(route.params.id as string)
  }
})
</script>

嵌套路由

通过文件夹结构创建嵌套路由:

pages/
  └── users/
      ├── index.vue      # 父路由组件
      └── [id]/
          ├── index.vue  # /users/:id
          ├── edit.vue   # /users/:id/edit
          └── posts/
              └── [postId].vue  # /users/:id/posts/:postId
<!-- pages/users/index.vue -->
<template>
  <div>
    <h1>用户管理</h1>
    <!-- 子路由渲染出口 -->
    <NuxtPage />
  </div>
</template>

捕获路由

用于匹配未明确声明的路由路径,支持两种匹配模式:

pages/
├── [...slug].vue    # 必需参数捕获 (必须包含参数)
└── [[...slug]].vue  # 可选参数捕获 (参数可选)

工作原理

  • [...slug]:匹配任意层级路径,参数会以数组形式保存在 $route.params.slug
  • [[...slug]]:同上,但允许零个参数(匹配父级路由)

典型应用场景

  1. 自定义404页面
  2. 全站路由监控
  3. 动态面包屑导航
  4. 兼容旧路由结构的重定向

配置示例

<script setup lang="ts">
definePageMeta({
  // 自定义正则匹配规则
  path: '/:slug(.*)*'
})
</script>

最佳实践

模块化组织:按功能模块组织页面,避免扁平化结构

pages/
├── auth/           # 认证模块
│   ├── login.vue
│   └── register.vue
├── dashboard/      # 控制台模块
│   ├── index.vue
│   └── analytics.vue
└── admin/          # 管理模块
    ├── users/
    └── settings/

路由元信息:使用 definePageMeta 定义页面元信息

<script setup lang="ts">
definePageMeta({
  title: '用户管理',
  description: '企业用户管理系统',
  requiresAuth: true,
  layout: 'admin'
})
</script>

路由工作机制

文件系统路由的实现原理

Nuxt 的文件系统路由是通过构建时的静态分析实现的。其核心工作流程如下:

  1. 构建时扫描:Nuxt 在构建过程中使用 @nuxt/kitscanDir 函数递归扫描 pages 目录
  2. 路由树构建:基于文件路径和命名约定,构建内部路由树数据结构
  3. 动态路由处理:识别 [param][...slug] 等动态路由模式,生成对应的路由参数配置
  4. 路由代码生成:在 .nuxt/routes.mjs 中生成最终的路由配置代码
// 内部生成的路由配置示例
export const routes = [
  {
    path: '/blog/:slug',
    component: () => import('~/pages/blog/[slug].vue'),
    meta: { /* 从 definePageMeta 提取的元数据 */ }
  }
]

性能优化机制

  • 懒加载:每个页面组件都被包装为动态导入,实现代码分割
  • 预加载:Nuxt 会预加载链接页面的组件,提升导航速度
  • 路由缓存:路由配置在构建时确定,运行时无需重新计算

与 Vue Router 的集成: Nuxt 在底层使用 Vue Router,但通过文件系统抽象简化了路由配置。构建时生成的路由配置会被传递给 Vue Router 实例,保持了 Vue 生态系统的兼容性。

components 目录与组件开发

components 目录实现了零配置的组件自动导入,提高开发效率。

目录结构设计

components/
├── Base/              # 基础组件
│   ├── Button.vue     # <BaseButton>
│   ├── Input.vue      # <BaseInput>
│   └── Modal.vue      # <BaseModal>
├── Form/              # 表单组件
│   ├── UserForm.vue   # <FormUserForm>
│   └── LoginForm.vue  # <FormLoginForm>
├── Layout/            # 布局组件
│   ├── Header.vue     # <LayoutHeader>
│   ├── Sidebar.vue    # <LayoutSidebar>
│   └── Footer.vue     # <LayoutFooter>
└── Common/            # 通用组件
    ├── Loading.vue    # <CommonLoading>
    └── Empty.vue      # <CommonEmpty>

组件命名约定

基础组件示例
表单组件示例
<!-- components/Base/Button.vue -->
<template>
  <button 
    :class="buttonClasses" 
    :disabled="disabled"
    @click="handleClick"
  >
    <slot />
  </button>
</template>

<script setup lang="ts">
interface Props {
  variant?: 'primary' | 'secondary' | 'danger'
  size?: 'sm' | 'md' | 'lg'
  disabled?: boolean
}

const props = withDefaults(defineProps<Props>(), {
  variant: 'primary',
  size: 'md',
  disabled: false
})

const emit = defineEmits<{
  click: [event: MouseEvent]
}>()

const buttonClasses = computed(() => {
  return [
    'btn',
    `btn-${props.variant}`,
    `btn-${props.size}`,
    {
      'btn-disabled': props.disabled
    }
  ]
})

const handleClick = (event: MouseEvent) => {
  if (!props.disabled) {
    emit('click', event)
  }
}
</script>

组件开发规范

composables 目录与 Hooks

composables 目录用于存放可复用的组合式函数,实现逻辑复用和状态管理。

目录结构规范

composables/
├── api/               # API相关hooks
│   ├── useAuth.ts     # 认证逻辑
│   ├── useUser.ts     # 用户数据管理
│   └── useProducts.ts # 产品数据管理
├── ui/                # UI交互hooks
│   ├── useModal.ts    # 模态框控制
│   ├── useToast.ts    # 消息提示
│   └── useLoading.ts  # 加载状态
├── utils/             # 工具类hooks
│   ├── useLocalStorage.ts
│   ├── useDebounce.ts
│   └── useThrottle.ts
└── business/          # 业务逻辑hooks
    ├── useCart.ts     # 购物车逻辑
    ├── useOrder.ts    # 订单处理
    └── usePayment.ts  # 支付流程

Composables 编写规范

  1. 命名规范
  • 使用 use 前缀 + 功能描述 (例: useCart)
  • 文件名采用小驼峰式命名 (例: useProductList.ts)
  • 类型定义使用 PascalCase 命名 (例: AuthState)
  1. 函数结构
// 1. 类型定义优先
interface ReturnType {
  state: Ref<DataType>
  actions: {
    fetchData: () => Promise<void>
    updateData: (payload: PayloadType) => void
  }
  computed: {
    formattedData: ComputedRef<string>
  }
}

// 2. 主函数实现
export const useFeature = (): ReturnType => {
  // 状态声明 (优先用 ref 替代 reactive)
  const state = ref<DataType>(initialValue)
  
  // 计算属性
  const formattedData = computed(() => format(state.value))
  
  // 方法实现
  const fetchData = async () => {
    try {
      // 业务逻辑...
    } catch (error) {
      // 统一错误处理
    }
  }

  // 返回结构化对象
  return {
    state,
    actions: { fetchData },
    computed: { formattedData }
  }
}
  1. 最佳实践
  • 类型安全: 为参数、返回值和内部状态提供完整类型定义
  • 响应式隔离: 在函数内部创建独立响应式状态
  • 生命周期: 使用 onMounted/onUnmounted 管理副作用
  • 自动导入: 确保 composables 目录符合 Nuxt 自动导入规范
  • 依赖注入: 复杂场景使用 provide/inject 实现层级通信
  • 测试友好: 通过参数注入替代直接导入依赖
  1. 性能优化
  • 避免在 composables 中创建不必要的响应式对象
  • 使用 debounce/throttle 包装高频操作
  • 大数据量使用 shallowRef 替代 ref
  • 及时清理事件监听器和定时器
  • 使用 computedAsync 处理异步计算
  1. 文档规范
/**
 * 用户认证功能 Hook
 * @param config 认证配置项
 * @returns {
 *  user: 当前用户信息,
 *  login: 登录方法,
 *  logout: 退出登录方法
 * }
 * @example
 * const { user, login } = useAuth()
 */
API请求Hook
UI状态Hook
业务逻辑Hook
// composables/api/useAuth.ts
import type { User } from '~/types'

export interface LoginCredentials {
  email: string
  password: string
}

export interface AuthState {
  user: Ref<User | null>
  isAuthenticated: ComputedRef<boolean>
  isLoading: Ref<boolean>
  error: Ref<string | null>
}

export const useAuth = (): AuthState & {
  login: (credentials: LoginCredentials) => Promise<void>
  logout: () => Promise<void>
  refresh: () => Promise<void>
} => {
  // 状态管理
  const user = ref<User | null>(null)
  const isLoading = ref(false)
  const error = ref<string | null>(null)
  
  // 计算属性
  const isAuthenticated = computed(() => !!user.value)
  
  // 登录方法
  const login = async (credentials: LoginCredentials) => {
    try {
      isLoading.value = true
      error.value = null
      
      const { data } = await $fetch<{ user: User; token: string }>('/api/auth/login', {
        method: 'POST',
        body: credentials
      })
      
      // 存储用户信息
      user.value = data.user
      
      // 存储token到cookie
      const token = useCookie('auth-token', {
        httpOnly: true,
        secure: true,
        sameSite: 'strict',
        maxAge: 60 * 60 * 24 * 7 // 7天
      })
      token.value = data.token
      
      // 跳转到首页
      await navigateTo('/')
    } catch (err: any) {
      error.value = err.data?.message || '登录失败'
      throw err
    } finally {
      isLoading.value = false
    }
  }
  
  // 退出登录
  const logout = async () => {
    try {
      await $fetch('/api/auth/logout', { method: 'POST' })
    } catch (err) {
      console.warn('登出请求失败:', err)
    } finally {
      // 清除本地状态
      user.value = null
      await navigateTo('/auth/login')
    }
  }
  
  // 刷新用户信息
  const refresh = async () => {
    try {
      const { data } = await $fetch<{ user: User }>('/api/auth/me')
      user.value = data.user
    } catch (err) {
      console.warn('获取用户信息失败:', err)
      user.value = null
    }
  }
  
  return {
    user: readonly(user),
    isAuthenticated,
    isLoading: readonly(isLoading),
    error: readonly(error),
    login,
    logout,
    refresh
  }
}

layouts 目录与布局系统

layouts 目录定义页面布局模板,提供一致的页面结构。

布局结构

layouts/
├── default.vue        # 默认布局
├── auth.vue          # 认证页面布局
├── admin.vue         # 管理后台布局
├── blog.vue          # 博客页面布局
└── error.vue         # 错误页面布局

布局组件实现

默认布局
管理后台布局
<!-- layouts/default.vue -->
<template>
  <div class="min-h-screen bg-gray-50">
    <!-- 顶部导航 -->
    <LayoutHeader />
    
    <!-- 主要内容区域 -->
    <main class="container mx-auto px-4 py-8">
      <slot />
    </main>
    
    <!-- 底部 -->
    <LayoutFooter />
    
    <!-- 全局组件 -->
    <CommonLoading v-if="$route.meta.loading" />
    <NotificationToast />
  </div>
</template>

<script setup lang="ts">
// 布局级别的逻辑
const { user } = useAuth()

// 监听路由变化,重置页面状态
watch(() => $route.path, () => {
  // 清除之前的错误状态
  clearError()
})
</script>

middleware 目录与中间件

middleware 目录存放路由中间件,用于路由跳转前的逻辑处理。

中间件类型与命名

middleware/
├── auth.ts           # 认证中间件
├── guest.ts          # 游客访问中间件
├── admin.ts          # 管理员权限中间件
├── subscription.ts   # 订阅验证中间件
└── maintenance.ts    # 维护模式中间件

中间件实现示例

认证中间件
权限中间件
全局中间件
// middleware/auth.ts
export default defineNuxtRouteMiddleware((to, from) => {
  const { isAuthenticated } = useAuth()
  
  if (!isAuthenticated.value) {
    return navigateTo('/auth/login', { 
      redirectTo: to.fullPath 
    })
  }
})

plugins 目录与插件系统

plugins 目录用于注册全局插件第三方库,在应用初始化阶段执行。

插件核心特性

插件开发规范

  1. 命名规范
  • 使用数字前缀控制执行顺序 (例: 01.pinia.ts)
  • 客户端插件添加 .client.ts 后缀
  • 服务端插件添加 .server.ts 后缀
  • 通用插件使用 .ts 扩展名
  1. 最佳实践
  • 优先使用 Nuxt 的 provide/inject 实现依赖注入
  • 避免在插件中执行耗时操作
  • 第三方库集成时添加必要的类型声明
  • 客户端插件应检查 process.client 环境
  • 服务端插件应避免污染全局对象

插件分类组织

plugins/
├── 01.pinia.client.ts        # 状态管理 (仅客户端)
├── 02.axios.ts               # HTTP客户端
├── 03.dayjs.ts               # 日期处理
├── 04.element-plus.client.ts # UI组件库 (仅客户端)
├── 05.analytics.client.ts    # 分析工具 (仅客户端)
└── directives.ts             # 自定义指令

插件实现规范

HTTP客户端插件
自定义指令插件
// plugins/axios.ts
import axios from 'axios'

export default defineNuxtPlugin(() => {
  // 创建axios实例
  const api = axios.create({
    baseURL: useRuntimeConfig().public.apiBase,
    timeout: 10000,
    withCredentials: true
  })
  
  // 请求拦截器
  api.interceptors.request.use(
    (config) => {
      // 添加认证token
      const token = useCookie('auth-token')
      if (token.value) {
        config.headers.Authorization = `Bearer ${token.value}`
      }
      
      // 添加请求ID用于追踪
      config.headers['X-Request-ID'] = crypto.randomUUID()
      
      return config
    },
    (error) => Promise.reject(error)
  )
  
  // 响应拦截器
  api.interceptors.response.use(
    (response) => response,
    async (error) => {
      const { status } = error.response || {}
      
      // 处理认证失效
      if (status === 401) {
        const { logout } = useAuth()
        await logout()
        return Promise.reject(error)
      }
      
      // 处理服务器错误
      if (status >= 500) {
        const toast = useToast()
        toast.error('服务器错误,请稍后重试')
      }
      
      return Promise.reject(error)
    }
  )
  
  // 提供给应用使用
  return {
    provide: {
      api
    }
  }
})

utils 目录与工具方法

utils 目录存放纯函数工具方法,提供可复用的业务逻辑。

工具方法分类

utils/
├── format/           # 格式化工具
│   ├── date.ts       # 日期格式化
│   ├── number.ts     # 数字格式化
│   └── text.ts       # 文本处理
├── validation/       # 验证工具
│   ├── rules.ts      # 验证规则
│   └── schemas.ts    # 验证模式
├── api/              # API工具
│   ├── request.ts    # 请求封装
│   └── cache.ts      # 缓存管理
└── business/         # 业务工具
    ├── permissions.ts # 权限判断
    ├── pricing.ts     # 价格计算
    └── analytics.ts   # 数据分析

工具方法实现

日期格式化工具
验证工具
// utils/format/date.ts
import dayjs from 'dayjs'
import relativeTime from 'dayjs/plugin/relativeTime'
import timezone from 'dayjs/plugin/timezone'

dayjs.extend(relativeTime)
dayjs.extend(timezone)

/**
 * 格式化日期
 */
export const formatDate = (
  date: string | Date | dayjs.Dayjs,
  format: string = 'YYYY-MM-DD HH:mm:ss'
): string => {
  return dayjs(date).format(format)
}

/**
 * 相对时间
 */
export const timeAgo = (date: string | Date | dayjs.Dayjs): string => {
  return dayjs(date).fromNow()
}

/**
 * 是否为今天
 */
export const isToday = (date: string | Date | dayjs.Dayjs): boolean => {
  return dayjs(date).isSame(dayjs(), 'day')
}

server 目录与后端 API

server 目录提供全栈开发能力,可以创建API路由和服务端逻辑。

Server 目录结构

server/
├── api/              # API路由
│   ├── auth/
│   │   ├── login.post.ts
│   │   ├── logout.post.ts
│   │   └── me.get.ts
│   ├── users/
│   │   ├── index.get.ts
│   │   ├── [id].get.ts
│   │   └── [id].put.ts
│   └── upload.post.ts
├── middleware/       # 服务端中间件
│   ├── auth.ts
│   └── cors.ts
└── utils/           # 服务端工具
    ├── jwt.ts
    ├── db.ts
    └── validation.ts

核心功能

  1. 文件式 API 路由:自动将 server/api 下的文件映射为 RESTful 端点
  2. 类型安全:自动生成 API 请求/响应类型定义
  3. HTTP 方法支持:通过文件后缀指定请求方法(.get.ts, .post.ts
  4. 服务端中间件:实现跨路由的通用逻辑处理
  5. 服务端工具:封装数据库操作、认证逻辑等可复用模块

API 路由开发规范

  1. 路由组织原则
    • 按业务模块创建子目录(如 /users
    • 单个文件处理单一资源操作
    • 文件命名遵循 [param].method.ts 格式
    • 复杂参数使用 [...].ts 捕获剩余参数
  2. 请求处理规范
    • 使用 defineEventHandler 包裹业务逻辑
    • 通过 getRouterParam 获取路径参数
    • 使用 readBody 读取请求体内容
    • 异步操作必须使用 async/await
  3. 错误处理标准
    • 使用 createError 创建标准错误响应
    • HTTP 状态码遵循 REST 规范:
      • 200: 成功请求
      • 400: 参数错误
      • 401: 未授权
      • 404: 资源不存在
      • 500: 服务端错误
    • 错误信息需脱敏处理
  4. 数据验证要求
    • 使用 validation.ts 中的校验工具
    • 路径参数必须验证有效性
    • 请求体需通过 Zod 模式校验
    • 响应数据需类型断言(as Type)
  5. 安全规范
    • 敏感操作需通过 auth 中间件
    • 数据库查询使用参数化查询
    • 响应数据必须脱敏处理
    • 使用 useRuntimeConfig 获取敏感配置
  6. 性能优化
    • 数据库查询需添加缓存机制(使用 useStorage
    • 批量操作实现分页处理
    • 复杂计算使用 Worker 线程
    • 避免同步阻塞操作
  7. 类型安全
    • 请求/响应类型需定义在 ~/types
    • 使用 $fetch 替代 axios 获得类型提示
    • 接口响应必须实现类型校验(如 isValidUser
    • 使用泛型定义 API 响应结构
用户资料接口
数据更新接口
// server/api/users/[id].get.ts
import { User } from '~/types'

export default defineEventHandler(async (event) => {
  // 获取运行时配置
  const config = useRuntimeConfig()
  
  // 从路径参数获取用户ID
  const userId = getRouterParam(event, 'id')
  
  // 数据库查询
  const user = await $fetch(`${config.dbBaseUrl}/users/${userId}`, {
    headers: { Authorization: `Bearer ${config.dbToken}` }
  })

  // 类型校验
  if (!isValidUser(user)) {
    throw createError({
      statusCode: 404,
      statusMessage: '用户不存在'
    })
  }

  // 数据脱敏
  return {
    id: user.id,
    name: user.name,
    avatar: user.avatar
  } as User
})

// 用户类型校验
function isValidUser(data: any): boolean {
  return !!data?.id && !!data?.name
}

中间件开发实践

// server/middleware/auth.ts
export default defineEventHandler((event) => {
  // JWT 验证逻辑
  const token = getHeader(event, 'Authorization')?.split(' ')[1]
  
  if (!token) {
    throw createError({
      statusCode: 401,
      statusMessage: '需要认证令牌'
    })
  }

  try {
    const payload = verifyJWT(token)
    event.context.user = payload
  } catch (error) {
    throw createError({
      statusCode: 403,
      statusMessage: '无效的认证令牌'
    })
  }
})

最佳实践

  1. 分层架构:保持路由处理简洁,复杂逻辑移至 server/utils
  2. 配置管理:敏感信息通过 .env 管理,使用 useRuntimeConfig() 获取
  3. 错误处理:统一错误格式,包含状态码和可读错误信息
  4. 性能优化:对高频接口添加缓存策略
  5. 安全防护:实施请求验证、速率限制和 SQL 注入防护

资源管理目录

assets 目录与静态资源

assets/ 目录用于存放需要被构建工具处理的静态资源。

资源目录结构

assets/
  ├── css/
   ├── main.css          # 主样式文件
   ├── variables.css     # CSS变量
   └── components.css    # 组件样式
  ├── scss/
   ├── _variables.scss   # SCSS变量
   ├── _mixins.scss      # SCSS混入
   └── main.scss         # 主SCSS文件
  ├── images/
   ├── logo.svg          # 矢量图标
   ├── hero-bg.jpg       # 背景图片
   └── icons/            # 图标集合
  └── fonts/
      ├── custom-font.woff2 # 自定义字体
      └── icons.ttf         # 图标字体

样式文件最佳实践

/* assets/css/variables.css */
:root {
  /* 主色系 */
  --color-primary: #3b82f6;
  --color-primary-dark: #1d4ed8;
  --color-primary-light: #93c5fd;
  
  /* 语义色彩 */
  --color-success: #10b981;
  --color-warning: #f59e0b;
  --color-error: #ef4444;
  --color-info: #06b6d4;
  
  /* 中性色 */
  --color-gray-50: #f9fafb;
  --color-gray-100: #f3f4f6;
  --color-gray-900: #111827;
  
  /* 间距系统 */
  --spacing-xs: 0.25rem;
  --spacing-sm: 0.5rem;
  --spacing-md: 1rem;
  --spacing-lg: 1.5rem;
  --spacing-xl: 2rem;
  
  /* 字体系统 */
  --font-size-xs: 0.75rem;
  --font-size-sm: 0.875rem;
  --font-size-base: 1rem;
  --font-size-lg: 1.125rem;
  --font-size-xl: 1.25rem;
  
  /* 圆角系统 */
  --border-radius-sm: 0.25rem;
  --border-radius-md: 0.375rem;
  --border-radius-lg: 0.5rem;
  --border-radius-xl: 0.75rem;
  
  /* 阴影系统 */
  --shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
  --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1);
  --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1);
}

在组件中使用资源

<template>
  <div class="hero">
    <!-- 使用assets中的图片 -->
    <img src="~/assets/images/hero-bg.jpg" alt="Hero Background" />
    
    <!-- 使用CSS变量 -->
    <button class="custom-button">点击我</button>
  </div>
</template>

<style scoped>
.hero {
  background-image: url('~/assets/images/hero-bg.jpg');
  background-size: cover;
  background-position: center;
}

.custom-button {
  background-color: var(--color-primary);
  color: white;
  padding: var(--spacing-md) var(--spacing-lg);
  border-radius: var(--border-radius-md);
  font-size: var(--font-size-base);
  box-shadow: var(--shadow-md);
  transition: all 0.2s ease;
}

.custom-button:hover {
  background-color: var(--color-primary-dark);
  box-shadow: var(--shadow-lg);
}
</style>

public 目录与公共文件

public/ 目录中的文件会被直接复制到网站根目录,无需构建处理。

public目录结构

public/
  ├── favicon.ico           # 网站图标
  ├── robots.txt            # 搜索引擎爬虫规则
  ├── sitemap.xml           # 网站地图
  ├── manifest.json         # PWA清单文件
  ├── images/
   ├── og-image.jpg      # 社交媒体分享图片
   └── screenshots/      # 应用截图
  ├── icons/
   ├── icon-192.png      # PWA图标
   └── icon-512.png      # PWA图标

PWA配置示例

// public/manifest.json
{
  "name": "Nuxt 企业级应用",
  "short_name": "NuxtApp",
  "description": "基于Nuxt3的企业级应用",
  "theme_color": "#3b82f6",
  "background_color": "#ffffff",
  "display": "standalone",
  "orientation": "portrait",
  "scope": "/",
  "start_url": "/",
  "icons": [
    {
      "src": "/icons/icon-192.png",
      "sizes": "192x192",
      "type": "image/png"
    },
    {
      "src": "/icons/icon-512.png",
      "sizes": "512x512",
      "type": "image/png"
    }
  ]
}

SEO配置文件

# public/robots.txt
User-agent: *
Allow: /

# 禁止爬取管理后台
Disallow: /admin/
Disallow: /api/

# 网站地图位置
Sitemap: https://your-domain.com/sitemap.xml

配置文件详解

nuxt.config.ts 配置文件

nuxt.config.ts 是Nuxt应用的核心配置文件,用于自定义应用行为。

基础配置示例

// nuxt.config.ts
export default defineNuxtConfig({
  // 开发工具
  devtools: { enabled: true },
  
  // TypeScript配置
  typescript: {
    strict: true,
    typeCheck: true
  },
  
  // CSS框架
  css: [
    '~/assets/css/main.css'
  ],
  
  // 模块配置
  modules: [
    '@nuxtjs/tailwindcss',
    '@pinia/nuxt',
    '@nuxt/content',
    '@nuxtjs/i18n'
  ],
  
  // 应用配置
  app: {
    // 全局头部配置
    head: {
      title: 'Nuxt 企业级应用',
      meta: [
        { charset: 'utf-8' },
        { name: 'viewport', content: 'width=device-width, initial-scale=1' },
        { name: 'description', content: '基于Nuxt3的企业级应用框架' }
      ],
      link: [
        { rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }
      ]
    }
  },
  
  // 路由配置
  router: {
    options: {
      scrollBehaviorType: 'smooth'
    }
  },
  
  // 运行时配置
  runtimeConfig: {
    // 服务端环境变量
    apiSecret: process.env.API_SECRET,
    
    // 公开的环境变量
    public: {
      apiBase: process.env.API_BASE_URL || '/api',
      appName: 'Nuxt App'
    }
  },
  
  // 构建配置
  build: {
    transpile: ['@headlessui/vue']
  },
  
  // Vite配置
  vite: {
    css: {
      preprocessorOptions: {
        scss: {
          additionalData: '@import "~/assets/scss/variables.scss";'
        }
      }
    }
  },
  
  // 服务端渲染配置
  ssr: true,
  
  // 静态生成配置
  nitro: {
    prerender: {
      routes: ['/sitemap.xml', '/robots.txt']
    }
  }
})

性能优化配置

// nuxt.config.ts - 性能优化版本
export default defineNuxtConfig({
  // 实验性功能
  experimental: {
    payloadExtraction: false,
    renderJsonPayloads: true
  },
  
  // 构建优化
  build: {
    // 代码分割
    splitChunks: {
      layouts: true,
      pages: true,
      commons: true
    }
  },
  
  // 资源优化
  optimization: {
    splitChunks: {
      chunks: 'all',
      cacheGroups: {
        vendor: {
          name: 'vendor',
          test: /[\\/]node_modules[\\/]/,
          chunks: 'all'
        }
      }
    }
  },
  
  // 压缩配置
  compression: {
    gzip: true,
    brotli: true
  },
  
  // 缓存配置
  routeRules: {
    '/': { prerender: true },
    '/admin/**': { ssr: false },
    '/api/**': { cors: true },
    '/blog/**': { 
      headers: { 'Cache-Control': 's-maxage=3600' }
    }
  }
})

app.vue 根组件

app.vue 是应用的根组件,所有页面都会在其内部渲染。

基础根组件

<!-- app.vue -->
<template>
  <div id="app">
    <!-- 全局加载状态 -->
    <LazyLoadingSpinner v-if="pending" />
    
    <!-- 页面内容 -->
    <NuxtLayout>
      <NuxtPage />
    </NuxtLayout>
    
    <!-- 全局组件 -->
    <LazyNotifications />
    <LazyModals />
  </div>
</template>

<script setup lang="ts">
// 全局META配置
useSeoMeta({
  titleTemplate: '%s | Nuxt 企业级应用',
  description: '基于Nuxt3构建的现代化企业级应用框架',
  ogImage: '/og-image.jpg',
  twitterCard: 'summary_large_image'
})

// 全局样式
useHead({
  htmlAttrs: {
    lang: 'zh-CN'
  },
  bodyAttrs: {
    class: 'min-h-screen bg-gray-50'
  }
})

// 全局状态管理
const { pending } = useLazyAsyncData('app-init', async () => {
  // 应用初始化逻辑
  const { initUser } = useAuth()
  await initUser()
  
  return true
})

// 全局错误处理
onErrorCaptured((error) => {
  console.error('应用错误:', error)
  // 发送错误到监控系统
  return false
})
</script>

<style>
html {
  scroll-behavior: smooth;
}

body {
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}

/* 全局过渡效果 */
.page-enter-active,
.page-leave-active {
  transition: all 0.3s ease;
}

.page-enter-from,
.page-leave-to {
  opacity: 0;
  transform: translateY(10px);
}
</style>

环境变量配置

.env文件配置

# .env
# 数据库配置
DATABASE_URL="postgresql://user:password@localhost:5432/mydb"

# API配置
API_SECRET="your-super-secret-key"
API_BASE_URL="https://api.example.com"

# 第三方服务
GITHUB_CLIENT_ID="your-github-client-id"
GITHUB_CLIENT_SECRET="your-github-client-secret"

# 邮件服务
SMTP_HOST="smtp.gmail.com"
SMTP_PORT="587"
SMTP_USER="your-email@gmail.com"
SMTP_PASS="your-app-password"

# 对象存储
AWS_ACCESS_KEY_ID="your-access-key"
AWS_SECRET_ACCESS_KEY="your-secret-key"
AWS_REGION="us-east-1"
AWS_BUCKET="your-bucket-name"

# 分析和监控
GOOGLE_ANALYTICS_ID="GA_MEASUREMENT_ID"
SENTRY_DSN="your-sentry-dsn"

环境变量使用示例

// 在服务端API中使用
// server/api/config.get.ts
export default defineEventHandler(() => {
  const config = useRuntimeConfig()
  
  return {
    apiSecret: config.apiSecret, // 服务端变量
    publicApiBase: config.public.apiBase // 公开变量
  }
})

// 在组件中使用公开变量
// components/ApiStatus.vue
<script setup lang="ts">
const config = useRuntimeConfig()
const apiBase = config.public.apiBase

const { data: status } = await $fetch(`${apiBase}/health`)
</script>

多环境配置

# .env.development
API_BASE_URL="http://localhost:3001/api"
NUXT_PUBLIC_APP_ENV="development"

# .env.staging  
API_BASE_URL="https://staging-api.example.com"
NUXT_PUBLIC_APP_ENV="staging"

# .env.production
API_BASE_URL="https://api.example.com"
NUXT_PUBLIC_APP_ENV="production"