一、项目初始化与目录结构设计

一个可维护、可扩展的项目结构是大型前端项目的基石。在启动项目时,花 30 分钟设计合理的目录结构,将节省未来数百小时的维护成本。

# 使用 create-vite 初始化
npm create vite@latest finboost-admin -- --template vue-ts

# 进入项目
cd finboost-admin

# 安装核心依赖
npm install vue-router@4 pinia axios element-plus
npm install -D @types/node sass unplugin-auto-import unplugin-vue-components

推荐的企业级目录结构:

finboost-admin/
├── .env                    # 环境变量(默认)
├── .env.development        # 开发环境变量
├── .env.production         # 生产环境变量
├── vite.config.ts          # Vite 构建配置
├── tsconfig.json           # TypeScript 配置
├── index.html              # HTML 入口
│
├── public/                 # 静态资源(不经过编译)
│   └── favicon.ico
│
└── src/
    ├── main.ts             # 应用入口
    ├── App.vue             # 根组件
    │
    ├── api/                # API 接口层
    │   ├── index.ts        # Axios 实例封装
    │   ├── modules/        # 按业务模块拆分
    │   │   ├── auth.ts     # 认证相关 API
    │   │   ├── order.ts    # 订单相关 API
    │   │   └── user.ts     # 用户相关 API
    │   └── types.ts        # API 类型定义
    │
    ├── assets/             # 编译时资源
    │   ├── styles/
    │   │   ├── variables.scss   # SCSS 变量
    │   │   ├── mixins.scss      # SCSS Mixin
    │   │   └── global.scss      # 全局样式
    │   └── images/
    │
    ├── components/         # 全局通用组件
    │   ├── common/         # 基础组件(Button, Modal 等)
    │   └── business/       # 业务组件
    │
    ├── composables/        # 组合式函数(Hooks)
    │   ├── useAuth.ts      # 认证逻辑
    │   ├── usePermission.ts # 权限控制
    │   └── useTable.ts     # 表格通用逻辑
    │
    ├── directives/         # 自定义指令
    │   └── permission.ts   # v-permission 权限指令
    │
    ├── layouts/            # 布局组件
    │   ├── DefaultLayout.vue
    │   └── AuthLayout.vue
    │
    ├── router/             # 路由配置
    │   ├── index.ts        # 路由实例
    │   ├── routes/         # 路由模块
    │   │   ├── auth.ts
    │   │   ├── dashboard.ts
    │   │   └── settings.ts
    │   └── guards.ts       # 路由守卫
    │
    ├── stores/             # Pinia 状态管理
    │   ├── user.ts         # 用户状态
    │   ├── app.ts          # 应用全局状态
    │   └── permission.ts   # 权限状态
    │
    ├── types/              # 全局类型定义
    │   ├── global.d.ts
    │   └── api.d.ts
    │
    ├── utils/              # 工具函数
    │   ├── request.ts      # HTTP 请求工具
    │   ├── storage.ts      # 本地存储封装
    │   └── validator.ts    # 表单验证
    │
    └── views/              # 页面组件(按模块)
        ├── auth/
        │   └── Login.vue
        ├── dashboard/
        │   └── Index.vue
        ├── orders/
        │   ├── List.vue
        │   └── Detail.vue
        └── settings/
            └── Index.vue
视图层 Views (Pages) + Layouts + Components 逻辑层 Composables (Hooks) + Directives 状态层 Pinia Stores + Router Guards 服务层 API Modules (Axios) + Utils 依赖 依赖 调用 前端分层架构 — 自上而下单向依赖,禁止反向引用 View → Composable → Store → Service,每层只依赖下一层

二、Vite 配置深度优化

// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { resolve } from 'path'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'

export default defineConfig({
  plugins: [
    vue(),
    AutoImport({
      resolvers: [ElementPlusResolver()],
      imports: ['vue', 'vue-router', 'pinia'],
      dts: 'src/auto-imports.d.ts',
    }),
    Components({
      resolvers: [ElementPlusResolver()],
      dts: 'src/components.d.ts',
    }),
  ],
  
  resolve: {
    alias: {
      '@': resolve(__dirname, 'src'),
      '@api': resolve(__dirname, 'src/api'),
      '@views': resolve(__dirname, 'src/views'),
      '@components': resolve(__dirname, 'src/components'),
    },
  },
  
  css: {
    preprocessorOptions: {
      scss: {
        additionalData: `@use "@/assets/styles/variables.scss" as *; @use "@/assets/styles/mixins.scss" as *;`,
      },
    },
  },
  
  server: {
    port: 5173,
    proxy: {
      '/api': {
        target: 'http://localhost:3004',
        changeOrigin: true,
        rewrite: (path) => path.replace(/^\/api/, ''),
      },
    },
  },
  
  build: {
    target: 'es2020',
    outDir: 'dist',
    assetsDir: 'assets',
    sourcemap: false,
    
    // 代码分割策略
    rollupOptions: {
      output: {
        manualChunks: {
          'vue-vendor': ['vue', 'vue-router', 'pinia'],
          'ui-vendor': ['element-plus'],
          'chart-vendor': ['echarts', 'vue-echarts'],
        },
      },
    },
    
    // 压缩配置
    minify: 'terser',
    terserOptions: {
      compress: {
        drop_console: true,
        drop_debugger: true,
      },
    },
    
    // 分块大小警告阈值
    chunkSizeWarningLimit: 1000,
  },
})

三、Pinia 状态管理最佳实践

// stores/user.ts — 组合式 API 风格(推荐)
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
import { getUserInfo, login, logout } from '@/api/modules/auth'
import type { UserInfo } from '@/types/api'
import { storage } from '@/utils/storage'

export const useUserStore = defineStore('user', () => {
  // ── State ──
  const token = ref(storage.get('token') || '')
  const userInfo = ref(null)
  
  // ── Getters ──
  const isLoggedIn = computed(() => !!token.value)
  const userName = computed(() => userInfo.value?.name || '未登录')
  const roles = computed(() => userInfo.value?.roles || [])
  
  // ── Actions ──
  async function doLogin(username: string, password: string) {
    const res = await login({ username, password })
    token.value = res.token
    storage.set('token', res.token)
    await fetchUserInfo()
  }
  
  async function fetchUserInfo() {
    const res = await getUserInfo()
    userInfo.value = res
  }
  
  async function doLogout() {
    await logout()
    token.value = ''
    userInfo.value = null
    storage.remove('token')
  }
  
  // 导出
  return { token, userInfo, isLoggedIn, userName, roles, doLogin, fetchUserInfo, doLogout }
})

四、Vue Router 路由设计模式

// router/index.ts
import { createRouter, createWebHistory } from 'vue-router'
import type { RouteRecordRaw } from 'vue-router'
import { setupGuards } from './guards'

// 路由模块化拆分
const routes: RouteRecordRaw[] = [
  {
    path: '/',
    component: () => import('@/layouts/DefaultLayout.vue'),
    redirect: '/dashboard',
    children: [
      {
        path: 'dashboard',
        name: 'Dashboard',
        component: () => import('@/views/dashboard/Index.vue'),
        meta: { title: '仪表盘', icon: 'Dashboard', roles: ['admin', 'manager'] },
      },
      {
        path: 'orders',
        name: 'OrderList',
        component: () => import('@/views/orders/List.vue'),
        meta: { title: '订单管理', keepAlive: true },
      },
    ],
  },
  {
    path: '/login',
    name: 'Login',
    component: () => import('@/views/auth/Login.vue'),
    meta: { title: '登录', noAuth: true },
  },
  {
    path: '/:pathMatch(.*)*',
    name: 'NotFound',
    component: () => import('@/views/error/404.vue'),
  },
]

const router = createRouter({
  history: createWebHistory(),
  routes,
  scrollBehavior: () => ({ top: 0 }),
})

// 注册全局守卫
setupGuards(router)

export default router

五、构建优化与分包策略

经过上述配置优化后,生产构建包体积通常可缩减 40%-60%:

优化项 优化前 优化后 效果
主包体积850 KB280 KB↓ 67%
整体构建时间45s18s↓ 60%
首屏加载时间3.2s0.8s↓ 75%
Lighthouse 评分6294↑ 52%
💡 工程化核心原则

坚持单向依赖原则:View 依赖 Composable → Composable 依赖 Store → Store 依赖 Service。严禁跨层引用和反向依赖。将通用逻辑抽取为 Composable,避免组件间代码复制。路由配置按业务模块拆分,使用动态 import 实现路由级代码分割。