Skip to content
目录

Vue3后台管理系统(一):开发环境搭建与项目初始化

一、开发环境准备

  1. Node.js 16+(推荐最新LTS版本)
  2. 包管理器:npm 8+(我更推荐使用pnpm)

二、使用Vite创建Vue3项目

bash
# 创建项目
pnpm create vite vue3-admin-template --template vue

# 进入项目目录
cd vue3-admin-template

# 安装依赖
pnpm install

三、项目基础配置

3.1 目录结构规划

|-- vue3-admin-template
    |-- src
        |-- api            # API接口管理
        |-- assets         # 静态资源
        |-- components     # 公共组件
        |-- composables    # 组合式功能(hooks)
        |-- directives     # 自定义指令
        |-- layout         # 布局组件
        |-- router         # 路由配置
        |-- stores         # Pinia状态管理
        |-- styles         # 全局样式
        |-- utils          # 工具函数
        |-- views          # 页面组件
        |-- App.vue        # 根组件
        |-- main.js        # 入口文件
    |-- .env.development   # 开发环境变量
    |-- .env.production    # 生产环境变量
    |-- .env.test          # 测试环境变量
    |-- vite.config.js     # Vite配置
    |-- package.json       # 项目配置
    |-- index.html         # HTML模板

3.2 Vite配置

javascript
// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue' // Vue3插件,使Vite支持.vue文件
import path from 'path' // Node.js内置模块,用于处理文件路径
import AutoImport from 'unplugin-auto-import/vite' // 自动导入API插件
import Components from 'unplugin-vue-components/vite' // 自动导入组件插件
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers' // Element Plus组件解析器
import viteCompression from 'vite-plugin-compression' // 构建压缩插件
import vueDevTools from 'vite-plugin-vue-devtools' // Vue DevTools增强插件

export default defineConfig({
  base: '/', // 部署应用的基础URL,通常设置为'/'表示根路径
  plugins: [
    vue(), // 启用Vue3插件
    vueDevTools(), // 增强Vue开发工具,提供更好的调试体验
    AutoImport({
      resolvers: [ElementPlusResolver()], // 使用Element Plus解析器
      imports: ['vue', 'vue-router', 'pinia'] // 自动导入Vue、Vue Router、Pinia API,无需手动import
      // 技巧:自动导入可以减少大量重复的import语句,使代码更简洁
    }),
    Components({
      resolvers: [ElementPlusResolver()] // 自动导入Element Plus组件,无需手动注册
      // 技巧:按需导入组件可以减少最终打包体积
    }),
    viteCompression({
      verbose: true, // 是否在控制台输出压缩结果
      disable: false, // 是否禁用压缩
      threshold: 10240, // 文件大小超过10KB才会被压缩
      algorithm: 'gzip', // 使用gzip算法
      ext: '.gz', // 生成的压缩包扩展名
      // 技巧:启用gzip压缩可以显著减少传输文件大小,提升加载速度
    })
  ],
  resolve: {
    alias: {
      '@': path.resolve(__dirname, 'src') // 路径别名,使用@代替src路径
      // 技巧:路径别名可以简化导入路径,避免使用繁琐的相对路径
    },
    extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue'], // 导入时可以省略的扩展名
  },
  css: {
    preprocessorOptions: {
      scss: {
        additionalData: `@use "@/styles/variables.scss" as *;`, // 全局注入SCSS变量
        // 技巧:这样可以在所有组件中直接使用变量,无需每个文件都import
      },
    },
  },
  server: {
    port: 3000, // 开发服务器端口
    open: true, // 启动时自动打开浏览器
  },
  build: {
    target: 'es2015', // 构建目标环境
    minify: 'terser', // 使用terser进行代码压缩
    terserOptions: {
      compress: {
        drop_debugger: true, // 移除debugger语句
        pure_funcs: ['console.log', 'console.debug', 'console.trace'], // 移除console语句
        // 技巧:生产环境中移除console语句可以减小打包体积并避免信息泄露
      },
      output: {
        comments: false, // 移除注释
      },
    },
    brotliSize: true, // 启用brotli压缩大小报告
    chunkSizeWarningLimit: 2000, // 警告体积超过2000KB的chunks
    assetsInlineLimit: 4096, // 小于4KB的资源将被内联为base64
    sourcemap: false, // 不生成sourcemap,减小构建体积
    assetsDir: `assets.${new Date().getTime()}`, // 资源目录添加时间戳避免缓存问题
    // 技巧:时间戳可以防止浏览器缓存旧资源
    rollupOptions: {
      output: {
        manualChunks: {
          'vendor': ['vue', 'vue-router', 'pinia'], // 将核心库打包到vendor chunk
          'alova': ['alova', 'alova/client'], // HTTP请求库单独打包
          'element-plus': ['element-plus'], // UI组件库单独打包
          'icons': ['@element-plus/icons-vue'], // 图标单独打包
          'utils': [
            'crypto-js',
            'nprogress',
            'autofit.js',
          ], // 工具库单独打包
          // 技巧:手动分chunk可以更好地利用缓存,避免一处修改导致所有文件失效
        },
        chunkFileNames: `js/[name].[hash].${new Date().getTime()}.js`, // chunk文件名格式
        entryFileNames: `js/[name].[hash].${new Date().getTime()}.js`, // 入口文件名格式
        // 技巧:添加时间戳可以确保每次构建生成不同的文件名,避免CDN或浏览器缓存问题
      }
    }
  }
})

这个配置包含了以下核心功能:

  • 路径别名(@指向src目录)
  • Element Plus组件和API的按需自动导入
  • 构建时的Gzip压缩
  • 开发服务器配置(端口、自动打开浏览器)
  • 构建优化(去除控制台日志、代码分割)
  • Vue DevTools增强插件
  • CSS预处理器配置

3.3 环境变量配置

# .env.development
NODE_ENV=development
VITE_APP_TITLE=Vue3后台管理系统(开发环境)
VITE_APP_API_BASE_URL=/api
VITE_APP_UPLOAD_URL=/api/upload

# .env.production
NODE_ENV=production
VITE_APP_TITLE=Vue3后台管理系统
VITE_APP_API_BASE_URL=https://api.example.com
VITE_APP_UPLOAD_URL=https://api.example.com/upload

# .env.test
NODE_ENV=test
VITE_APP_TITLE=Vue3后台管理系统(测试环境)
VITE_APP_API_BASE_URL=https://test-api.example.com
VITE_APP_UPLOAD_URL=https://test-api.example.com/upload

# API 配置(开发环境使用测试API)
VITE_API_BASE_URL=https://filling-service.xjy2.cn

# 文件上传配置(开发环境使用测试服务)
VITE_OSS_BASE_URL=//oss-test.xjy0.cn/api/upload

# 环境配置
VITE_APP_ENV=development

在代码中,可以通过import.meta.env访问这些环境变量:

javascript
console.log(import.meta.env.VITE_APP_API_BASE_URL)

四、核心依赖安装

4.1 安装Vue全家桶

bash
# Vue核心库、路由、状态管理
pnpm add vue vue-router@4 pinia

4.2 UI组件库

本项目使用Element Plus作为UI组件库:

bash
# 安装Element Plus及图标库
pnpm add element-plus @element-plus/icons-vue

4.3 工具库

我们还需要一些常用的工具库:

bash
# HTTP请求库 - 这里使用alova替代axios
pnpm add alova

# 加密工具
pnpm add crypto-js encryptlong

# Excel数据处理
pnpm add exceljs file-saver

# 进度条
pnpm add nprogress

# 自适应布局
pnpm add autofit.js

五、项目的初始化结构

5.1 入口文件

javascript
// src/main.js
import { createApp } from 'vue' // 创建Vue应用的核心API
import { createPinia } from 'pinia' // 创建Pinia状态管理实例
import ElementPlus from 'element-plus' // Element Plus组件库
import * as ElementPlusIconsVue from '@element-plus/icons-vue' // 引入所有Element Plus图标
import autofit from 'autofit.js' // 自适应布局工具
import 'element-plus/dist/index.css' // Element Plus样式
import './styles/index.scss' // 全局样式
import App from './App.vue' // 根组件
import router from './router' // 路由
import zhCn from 'element-plus/dist/locale/zh-cn.mjs' // Element Plus中文语言包
import { setupPermissionDirective } from './directives/permission' // 权限指令

// 应用实例
const app = createApp(App)
// Pinia 实例
const pinia = createPinia()
app.use(pinia)
// 按需引入核心图标
const coreIcons = ['Plus', 'Search', 'Upload', 'ArrowDown', 'Close'] 
// 优先注册核心图标
for (const key of coreIcons) {
  if (ElementPlusIconsVue[key]) {
    app.component(key, ElementPlusIconsVue[key])
  }
}
// 在页面空闲时注册其他图标
if (window.requestIdleCallback) {
  requestIdleCallback(() => {
    for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
      if (!coreIcons.includes(key)) {
        app.component(key, component)
      }
    }
  })
} else {
  // 兼容不支持requestIdleCallback的浏览器
  setTimeout(() => {
    for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
      if (!coreIcons.includes(key)) {
        app.component(key, component)
      }
    }
  }, 300)
}
// 技巧:图标分批注册性能优化
// 1. 先注册核心常用图标,确保首屏渲染所需图标立即可用
// 2. 其他图标在浏览器空闲时再注册,避免阻塞主线程
// 3. 使用requestIdleCallback API可以在浏览器空闲时执行低优先级任务
// 4. 提供降级方案确保兼容性,setTimeout作为备选方案

// 注册权限指令
setupPermissionDirective(app)
// 安装其他插件
app.use(ElementPlus, {
  locale: zhCn, // 设置Element Plus为中文
})
app.use(router)
// 初始化 autofit
autofit.init({
  el: '#app', // 目标元素
  dw: 1920,   // 设计图宽度
  dh: 1040,   // 设计图高度,根据设计图按照实际情况调整
  resize: true, // 是否在窗口大小变化时重新计算
  transition: 0.3, // 过渡动画时间(秒)
  limit: 0.1, // 最小缩放比例限制
  cssMode: 'scale', // 使用scale方式缩放,保持比例
  allowScroll: false, // 禁止滚动,保持整体布局
  // 技巧:使用autofit.js实现全局自适应缩放
  // 1. 适合大屏项目和管理系统,可以保持一致的展示效果
  // 2. 不同于自适应布局,整体缩放可以保持元素间的相对位置不变
  // 3. 结合limit参数可以避免过度缩小导致内容不可读
})

// 页面挂载
app.mount('#app')
javascript
// .prettierrc
{
  "semi": false, // 不使用分号结尾
  "singleQuote": true, // 使用单引号代替双引号
  "useTabs": true, // 使用Tab而非空格进行缩进
  "tabWidth": 2, // Tab等效的空格数为2
  "trailingComma": "es5", // ES5中有效的尾随逗号(数组、对象等)
  "printWidth": 100, // 行宽为100字符,超出换行
  "bracketSpacing": true, // 对象字面量中的括号前后添加空格
  "arrowParens": "always", // 箭头函数参数始终使用括号,即使只有一个参数
  "endOfLine": "auto", // 自动识别换行符
  "htmlWhitespaceSensitivity": "css", // HTML空白符敏感度按CSS display属性决定
  "insertPragma": false, // 不在文件顶部插入@prettier特殊注释
  "bracketSameLine": false, // HTML标签的闭合括号不与最后一个属性在同一行
  "quoteProps": "as-needed", // 对象属性只在必要时使用引号
  "vueIndentScriptAndStyle": false // 不缩进Vue文件中的script和style标签内容
}

"scripts": {
  "format": "prettier --write \"src/**/*.{js,jsx,vue,scss,css,json,md}\""
}

Released under the MIT License.