Vue CLI
大白话解释: Vue CLI 就像"项目模板生成器"。手动搭建 Vue 项目需要配置 Webpack、Babel、ESLint、Router、Vuex 等很多东西,Vue CLI 帮你一键生成,开箱即用。
为什么用脚手架?
- 省时间:不用手动配置 Webpack、Babel 等复杂工具
- 最佳实践:官方推荐的项目结构和配置
- 插件生态:需要什么功能,装个插件就行
Vue CLI vs Vite:
- Vue CLI:基于 Webpack,功能成熟但启动慢,Vue 2 项目用
- Vite:基于 ESBuild,启动快,Vue 3 项目推荐
⚠️ Vue CLI 已进入维护模式(不再开发新功能),Vue 3 项目推荐使用 Vite。详见 Vue CLI → Vite 迁移指南。
Vue CLI 是 Vue 官方提供的脚手架工具,基于 Webpack 封装,用于快速创建、开发和构建 Vue 项目。Vue CLI 5 同时支持 Vue 2 和 Vue 3,但 Vue 3 新项目已推荐使用 Vite。
核心概念
Vue CLI 架构
┌─────────────────────────────────────────────────────────┐
│ vue-cli-service │
│ │
│ vue.config.js ──→ Webpack 配置合并 ──→ 最终 Webpack 配置 │
│ │
│ configureWebpack(简单合并) │
│ chainWebpack(链式 API 修改) │
│ │
│ 内置 loader:js / vue / css / scss / less / ts / 图片 │
│ 内置插件:html / define / copy / preload / prefetch │
└─────────────────────────────────────────────────────────┘configureWebpack vs chainWebpack
| 方式 | 用法 | 适用场景 |
|---|---|---|
configureWebpack | 直接写 Webpack 配置对象,自动 merge | 简单配置(加 alias、externals) |
chainWebpack | 链式 API,可精确修改已有配置 | 复杂操作(删插件、改 loader、条件判断) |
💡 两者可以同时使用,
chainWebpack后执行,会覆盖configureWebpack的同名配置。
安装与版本管理
# 全局安装
npm install -g @vue/cli
# 查看版本
vue --version
# 升级到最新
npm update -g @vue/cli
# 安装指定大版本
npm install -g @vue/cli@4 # 4.x 主要用于 Vue 2
npm install -g @vue/cli@5 # 5.x 同时支持 Vue 2 和 Vue 3
# 从旧版 webpack 模板迁移(兼容 vue init 语法)
npm install -g @vue/cli-init
vue init webpack my-project创建项目
交互式创建
vue create my-project可选预设:
- Default (Vue 3) — Vue 3 + Babel + ESLint
- Default (Vue 2) — Vue 2 + Babel + ESLint
- Manually select features — 手动勾选特性
手动可选特性:
- Babel — ES6+ 语法转译
- TypeScript — TS 支持
- Router — vue-router
- Vuex — 状态管理
- CSS Pre-processors — Sass / Less / Stylus
- Linter / Formatter — ESLint / Prettier
- Unit Testing — Jest / Mocha
- E2E Testing — Cypress / Nightwatch
- PWA — 渐进式 Web 应用
命令行参数
# 指定预设(跳过交互)
vue create my-project --preset __default_vue_2__
# 从 JSON 文件加载预设
vue create my-project --preset ./preset.json
# 指定包管理器
vue create my-project --packageManager yarn
# 跳过 Git 初始化
vue create my-project --no-git预设文件
⚠️ JSON 不支持注释,实际使用时需去掉
//注释部分。
{
"useConfigFiles": true, // true: 每个插件单独配置文件;false: 全部写入 package.json
"plugins": {
"@vue/cli-plugin-babel": {},
"@vue/cli-plugin-eslint": {
"config": "base", // base | airbnb | standard
"lintOn": ["save"] // 保存时检查:["save"] | ["save", "commit"]
},
"@vue/cli-plugin-router": {
"historyMode": true // true: history 模式;false: hash 模式
},
"@vue/cli-plugin-vuex": {},
"@vue/cli-plugin-typescript": {
"classComponent": true, // 是否使用 class 风格组件
"useTsWithBabel": true // 是否与 Babel 配合使用
},
"@vue/cli-plugin-css-preprocessor": {
"sass": true // sass | less | stylus
}
},
"vueVersion": "2" // "2" | "3"
}项目开发命令
package.json 中的 scripts
{
"scripts": {
"serve": "vue-cli-service serve", // 启动开发服务器
"build": "vue-cli-service build", // 生产构建
"lint": "vue-cli-service lint", // ESLint 检查修复
"test:unit": "vue-cli-service test:unit", // 单元测试
"test:e2e": "vue-cli-service test:e2e" // E2E 测试
}
}开发服务器
npm run serve # 启动(默认 localhost:8080)
npm run serve -- --port 3000 # 指定端口
npm run serve -- --open # 自动打开浏览器
npm run serve -- --host 0.0.0.0 # 允许局域网访问(手机调试)
npm run serve -- --https # 启用 HTTPS
npm run serve -- --port 3000 --open --host 0.0.0.0 # 组合使用生产构建
npm run build # 构建到 dist/
npm run build -- --report # 生成包体积分析报告(report.html)
npm run build -- --modern # 生成两份包:现代浏览器(ES Module)+ 旧浏览器兼容
npm run build -- --modern app # 仅生成现代浏览器包
npm run build -- --no-clean # 构建前不清空 dist/代码检查
npm run lint # ESLint 检查并自动修复
npx vue-cli-service lint --no-fix # 仅检查,不修复
npx vue-cli-service lint --fix src/ # 只修复指定目录vue inspect(查看最终 Webpack 配置)
vue inspect # 输出完整 Webpack 配置
vue inspect --mode development # 开发环境配置
vue inspect --mode production # 生产环境配置
vue inspect --rule css # 只看 css 相关 loader
vue inspect --plugin html # 只看 html 插件配置
vue inspect > resolved.config.js # 输出到文件方便查看插件管理
vue add typescript # 添加 TypeScript 支持
vue add router # 添加 vue-router
vue add vuex # 添加 Vuex
vue add sass # 添加 Sass 预处理器
vue add less # 添加 Less 预处理器
vue add @vue/cli-plugin-eslint # 添加 ESLint
vue add @vue/cli-plugin-unit-jest # 添加 Jest 单元测试
vue add @vue/cli-plugin-e2e-cypress # 添加 Cypress E2E 测试
vue add @vue/cli-plugin-pwa # 添加 PWA 支持⚠️
vue add会修改项目文件(可能覆盖已有代码),建议在 Git 提交后再执行。
vue.config.js 完整配置
基本配置
// vue.config.js
const { defineConfig } = require('@vue/cli-service')
const path = require('path')
module.exports = defineConfig({
// ========== 部署路径 ==========
// 默认 '/',部署到子路径时需要修改
// 例:部署到 https://example.com/my-app/ → 设为 '/my-app/'
publicPath: process.env.NODE_ENV === 'production' ? '/my-app/' : '/',
// ========== 构建输出 ==========
outputDir: 'dist', // 构建输出目录(相对于项目根目录)
assetsDir: 'static', // 静态资源子目录(相对于 outputDir)
indexPath: 'index.html', // 生成的 index.html 文件名
filenameHashing: true, // 文件名是否带哈希(用于缓存更新)
// 设为 false 时所有文件名不带 hash
// ========== 多页面模式 ==========
// 默认是单页面(SPA),配置 pages 后变为多页面
// 单页面时不需要配置此项
pages: undefined,
// /*
// 多页面示例:
// pages: {
// index: {
// entry: 'src/main.js', // 入口文件
// template: 'public/index.html', // HTML 模板
// filename: 'index.html', // 输出文件名
// title: '首页', // HTML 标题
// chunks: ['chunk-vendors', 'chunk-common', 'index'] // 包含的 chunk
// },
// admin: {
// entry: 'src/admin/main.js',
// template: 'public/admin.html',
// filename: 'admin.html',
// title: '管理后台',
// chunks: ['chunk-vendors', 'chunk-common', 'admin']
// }
// }
// */
// ========== 编译器选择 ==========
// false(默认):使用 runtime-only 编译器,体积更小约 30%
// true:使用完整编译器,支持 template 选项和字符串模板
// 一般不需要开启,除非用到 runtime 编译器的特性
runtimeCompiler: false,
// ========== 依赖转译 ==========
// 默认只转译 src 目录,node_modules 中的 ES6+ 代码不转译
// 如果某个第三方库发布了 ES6+ 语法,需要在这里指定
transpileDependencies: [
'vue-echarts', // 这个库用了 ES6+ 语法
'resize-detector'
],
// ========== Source Map ==========
// 生产环境是否生成 Source Map
// true:可调试线上代码,但暴露源码,且增大构建体积
// false:不生成,推荐生产环境关闭
productionSourceMap: false,
// ========== ESLint ==========
// 保存时是否检查
// true:保存时检查 false:不检查
// 'error':保存时报错(阻断编译) 'warning':保存时警告
// process.env.NODE_ENV !== 'production':仅开发环境检查
lintOnSave: process.env.NODE_ENV !== 'production',
// ========== 跨域设置 ==========
// HTML 中 <link> 和 <script> 的 crossorigin 属性
// 'anonymous':不发送凭据 'use-credentials':发送凭据
crossorigin: undefined,
// ========== 子资源完整性 ==========
// 为生成的 <link> 和 <script> 添加 integrity 属性
integrity: false,
})开发服务器配置(devServer)
module.exports = {
devServer: {
// 端口号,默认 8080
port: 8080,
// 主机地址
// 'localhost':仅本机可访问
// '0.0.0.0':局域网可访问(手机调试、团队联调)
host: 'localhost',
// 启动后自动打开浏览器
open: true,
// 是否启用 HTTPS(自动生成证书)
https: false,
// 编译错误时是否在浏览器中显示遮罩层
overlay: {
warnings: false, // 警告不遮罩
errors: true // 错误显示遮罩
},
// ========== 代理配置(解决跨域) ==========
// 原理:开发服务器作为中间人,转发请求到后端
// 浏览器 → devServer(:8080) → 后端(:3000)
// 因为是服务器间请求,不存在跨域问题
proxy: {
// 简单代理:/api/* → http://localhost:3000/api/*
'/api': {
target: 'http://localhost:3000', // 后端地址
changeOrigin: true // 修改请求头中的 Host 为目标地址
},
// 路径重写:/api/v2/* → http://localhost:3000/v2/*
// 适用于后端接口没有 /api 前缀的情况
'/api/v2': {
target: 'http://localhost:3000',
changeOrigin: true,
pathRewrite: {
'^/api/v2': '/v2' // 正则替换路径
}
},
// WebSocket 代理
'/ws': {
target: 'ws://localhost:3000',
ws: true, // 启用 WebSocket 代理
changeOrigin: true
},
// 多后端代理(微服务架构)
'/user': {
target: 'http://user-service:3001', // 用户服务
changeOrigin: true
},
'/order': {
target: 'http://order-service:3002', // 订单服务
changeOrigin: true
}
},
// 自定义中间件(mock 数据)
// ⚠️ onBeforeSetupMiddleware 在 webpack-dev-server v5 中已废弃,
// 推荐使用 setupMiddlewares 替代(Vue CLI 5.0.8+ 支持)
// 以下为旧写法,保留仅供参考:
onBeforeSetupMiddleware(devServer) {
devServer.app.get('/api/mock/user', (req, res) => {
res.json({ code: 0, data: { name: '张三', age: 25 } })
})
},
// 推荐写法(webpack-dev-server v5+):
// setupMiddlewares(middlewares, devServer) {
// devServer.app.get('/api/mock/user', (req, res) => {
// res.json({ code: 0, data: { name: '张三', age: 25 } })
// })
// return middlewares
// },
// Vue CLI 4 写法:
// before(app) {
// app.get('/api/mock/user', (req, res) => {
// res.json({ code: 0, data: { name: '张三', age: 25 } })
// })
// },
// 热更新(默认 true)
hot: true,
// 文件监听选项(Docker / WSL 环境下热更新失效时开启 polling)
watchOptions: {
poll: false, // true 或 1000:轮询模式
aggregateTimeout: 300 // 防抖时间(ms)
}
}
}configureWebpack(简单合并)
// 适合简单配置,直接写 Webpack 配置对象
// Vue CLI 会用 webpack-merge 自动合并,不会覆盖已有配置
const path = require('path')
module.exports = {
configureWebpack: {
// 路径别名
resolve: {
alias: {
'@': path.resolve(__dirname, 'src'),
'@components': path.resolve(__dirname, 'src/components'),
'@views': path.resolve(__dirname, 'src/views'),
'@utils': path.resolve(__dirname, 'src/utils'),
'@assets': path.resolve(__dirname, 'src/assets'),
'@api': path.resolve(__dirname, 'src/api')
},
// 省略的扩展名(从左到右优先级递减)
extensions: ['.js', '.vue', '.json', '.ts', '.tsx']
},
// 外部依赖(CDN 引入,不打包进 bundle)
// key: 包名(import 时用的名字)
// value: 全局变量名(CDN 暴露到 window 上的变量)
externals: {
vue: 'Vue', // <script src="vue.js"> → window.Vue
'vue-router': 'VueRouter', // <script src="vue-router.js"> → window.VueRouter
vuex: 'Vuex', // <script src="vuex.js"> → window.Vuex
axios: 'axios', // <script src="axios.js"> → window.axios
echarts: 'echarts', // <script src="echarts.js"> → window.echarts
lodash: '_', // <script src="lodash.js"> → window._
moment: 'moment' // <script src="moment.js"> → window.moment
},
// 性能提示配置
performance: {
hints: process.env.NODE_ENV === 'production' ? 'warning' : false,
maxAssetSize: 512000, // 单个资源超过 500KB 警告
maxEntrypointSize: 512000 // 入口 chunk 超过 500KB 警告
}
}
}chainWebpack(链式 API)
// 适合复杂操作:删除/替换/条件修改已有配置
// 基于 webpack-chain 库,支持链式调用
const path = require('path')
module.exports = {
chainWebpack: config => {
// ========== 修改 HTML 插件 ==========
config.plugin('html').tap(args => {
args[0].title = 'My App' // 修改 <title>
return args
})
// ========== 路径别名 ==========
config.resolve.alias
.set('@', path.resolve(__dirname, 'src'))
.set('@components', path.resolve(__dirname, 'src/components'))
// ========== 删除预加载插件 ==========
// prefetch:浏览器空闲时预加载,可能浪费带宽
// preload:当前路由需要的资源预加载
config.plugins.delete('prefetch')
config.plugins.delete('preload')
// ========== 修改 SVG loader ==========
// 默认 SVG 被 file-loader 处理
// 排除 src/icons 目录(用 svg-sprite-loader 处理图标)
config.module
.rule('svg')
.exclude.add(path.resolve(__dirname, 'src/icons'))
.end()
// 为 src/icons 添加 svg-sprite-loader
config.module
.rule('icons')
.test(/\.svg$/)
.include.add(path.resolve(__dirname, 'src/icons'))
.end()
.use('svg-sprite-loader')
.loader('svg-sprite-loader')
.options({ symbolId: 'icon-[name]' }) // symbol ID 格式
.end()
// ========== 图片压缩 ==========
// 需要安装:npm install -D image-webpack-loader
config.module
.rule('images')
.test(/\.(png|jpe?g|gif|webp|svg)(\?.*)?$/)
.use('image-webpack-loader')
.loader('image-webpack-loader')
.options({
mozjpeg: { progressive: true, quality: 65 }, // JPEG 压缩
optipng: { enabled: false }, // PNG 压缩(慢)
pngquant: { quality: [0.65, 0.90], speed: 4 }, // PNG 压缩(快)
gifsicle: { interlaced: false } // GIF 压缩
})
.end()
// ========== 代码分割 ==========
config.optimization.splitChunks({
chunks: 'all', // 对所有 chunk 生效(initial / async / all)
cacheGroups: {
// 第三方库单独打包
vendors: {
name: 'chunk-vendors',
test: /[\\/]node_modules[\\/]/,
priority: -10, // 优先级(数值越大越优先)
chunks: 'initial' // 只对同步导入的生效
},
// Element UI 单独打包(体积大,按需加载时有用)
elementUI: {
name: 'chunk-elementUI',
priority: 20, // 优先级高于 vendors
test: /[\\/]node_modules[\\/]element-ui[\\/]/
},
// ECharts 单独打包
echarts: {
name: 'chunk-echarts',
priority: 20,
test: /[\\/]node_modules[\\/]echarts[\\/]/
},
// 业务公共代码(被 2 个以上 chunk 引用的模块)
common: {
name: 'chunk-common',
minChunks: 2, // 最少被引用次数
priority: -20,
chunks: 'initial',
reuseExistingChunk: true // 已存在则复用
}
}
})
// ========== 生产环境优化 ==========
if (process.env.NODE_ENV === 'production') {
// 移除 console.log 和 debugger
config.optimization.minimizer('terser').tap(args => {
args[0].terserOptions.compress.drop_console = true // 移除所有 console
args[0].terserOptions.compress.drop_debugger = true // 移除 debugger
return args
})
}
}
}CSS 预处理器配置
module.exports = {
css: {
// 是否将 CSS 提取为独立文件
// true(生产默认):提取为 .css 文件,利于缓存
// false(开发默认):以 <style> 标签注入,支持热更新
extract: process.env.NODE_ENV === 'production',
// 是否生成 CSS Source Map
sourceMap: false,
// CSS Modules 的类名转换方式
// true(默认):类名驼峰转换(.btn-primary → btnPrimary)
// false:保持原始类名
requireModuleExtension: true,
// 各预处理器的 Loader 配置
loaderOptions: {
css: {
// CSS Modules 配置
modules: {
// 类名格式:[name]文件名 [hash:base64:5]5位哈希
localIdentName: '[name]-[hash:base64:5]'
}
},
less: {
// 全局注入 Less 变量(每个 .less 文件自动引入)
// 不需要在每个文件手动 @import
additionalData: `@import "@/styles/variables.less";`
},
sass: {
// 全局注入 Sass 变量(Sass 新语法用 @use)
additionalData: `@use "@/styles/variables" as *;`
},
scss: {
// 全局注入 SCSS 变量
additionalData: `@import "@/styles/variables.scss";`
},
postcss: {
// PostCSS 插件(也可用 postcss.config.js 单独配置)
plugins: [
require('postcss-px-to-viewport')({
viewportWidth: 375, // 设计稿宽度
unitPrecision: 5, // 小数位数
viewportUnit: 'vw' // 转换单位
})
]
}
}
}
}入口文件示例(main.js)
⚠️ 以下为 Vue 2 语法。Vue 3 使用
createApp()创建应用,不再支持Vue.component全局注册、Vue.directive、Vue.filter、Vue.prototype等 API。
// src/main.js —— Vue 2 写法
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import dayjs from 'dayjs'
// 全局样式
import '@/styles/global.scss'
// 全局组件(如需要)
import BaseButton from '@/components/common/BaseButton.vue'
Vue.component('BaseButton', BaseButton)
// 全局指令(如需要)
Vue.directive('focus', {
inserted: el => el.focus()
})
// 全局过滤器(如需要)
// ⚠️ Vue 3 已移除 filter API,建议用 computed 或函数替代
Vue.filter('dateFormat', (value, format = 'YYYY-MM-DD') => {
return dayjs(value).format(format)
})
// 关闭生产提示
Vue.config.productionTip = false
new Vue({
router, // 注入路由
store, // 注入 Vuex
render: h => h(App)
}).$mount('#app')环境变量
Vue CLI 使用 VUE_APP_ 前缀暴露环境变量到客户端。Vite 项目使用 VITE_ 前缀,详见 环境变量管理。
文件命名与加载顺序
.env 所有环境都会加载
.env.local 所有环境,git 忽略(本地覆盖)
.env.development 仅 development 模式
.env.development.local 仅 development,git 忽略
.env.production 仅 production 模式
.env.production.local 仅 production,git 忽略优先级(从低到高):.env → .env.local → .env.[mode] → .env.[mode].local
命名规则
# ✅ VUE_APP_ 开头 → 注入客户端(process.env.VUE_APP_XXX)
VUE_APP_API_URL=https://api.example.com
VUE_APP_TITLE=My App
# ✅ 内置变量(无需定义,自动可用)
NODE_ENV=development # 自动设置:serve → development,build → production,test → test
BASE_URL=/ # 等于 publicPath 配置
# ❌ 不以 VUE_APP_ 开头 → 仅 Node.js 环境可用(服务端渲染、构建脚本)
# 注意:NODE_ENV 和 BASE_URL 由 Vue CLI 自动设置,不要手动定义在 .env 中
DB_HOST=localhost # 客户端代码中无法访问
SECRET_KEY=xxx # 不会暴露到前端按环境构建
npm run serve # 默认加载 .env.development
npm run build # 默认加载 .env.production
npm run serve --mode test # 加载 .env.test
npm run build --mode staging # 加载 .env.staging使用示例
# .env.development
VUE_APP_BASE_URL=http://localhost:3000
VUE_APP_ENV=development
# .env.production
VUE_APP_BASE_URL=https://api.example.com
VUE_APP_ENV=production
VUE_APP_CDN_URL=https://cdn.example.com// src/utils/request.js
import axios from 'axios'
const service = axios.create({
baseURL: process.env.VUE_APP_BASE_URL, // 根据环境自动切换
timeout: 10000
})
export default service<!-- public/index.html -->
<title><%= VUE_APP_TITLE %></title>
<link rel="icon" href="<%= BASE_URL %>favicon.ico">静态资源处理
src/assets/ vs public/
| 特性 | src/assets/ | public/ |
|---|---|---|
| Webpack 处理 | ✅ 编译、压缩、哈希 | ❌ 原样复制到 dist/ |
| 引用方式 | import / require() / 相对路径 | 绝对路径字符串 |
| 小图优化 | ✅ 自动 base64 内联 | ❌ 不处理 |
| 适用场景 | 组件级资源、需要优化的图片 | 第三方库、favicon、manifest |
<template>
<!-- src/assets 下的图片:Webpack 处理后输出带哈希的路径 -->
<img src="./assets/logo.png">
<!-- 动态路径:必须用 require() -->
<img :src="require('./assets/' + iconName + '.png')">
<!-- public 下的资源:直接用绝对路径 -->
<img src="/images/banner.jpg">
</template>
<script>
// JS 中引入 src/assets
import logo from './assets/logo.png'
console.log(logo) // 输出: /img/logo.3f8a2b12.png(带哈希)
// JS 中引用 public 下的资源
const url = `${process.env.BASE_URL}data/config.json`
</script>部署配置
Nginx 部署
server {
listen 80;
server_name example.com;
root /var/www/my-app/dist;
# history 模式必须配置:所有路径回退到 index.html
# 否则刷新页面会 404
location / {
try_files $uri $uri/ /index.html;
}
# 静态资源长期缓存(文件名带 hash,更新后自动失效)
location /static {
expires 1y; # 缓存 1 年
add_header Cache-Control "public, immutable"; # 不再验证
}
# API 反向代理
location /api {
proxy_pass http://localhost:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}子路径部署
// vue.config.js
// 部署到 https://example.com/my-app/
module.exports = {
publicPath: '/my-app/'
}Docker 部署
FROM nginx:alpine
COPY dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]迁移到 Vite
配置对照
| 功能 | vue.config.js | vite.config.ts |
|---|---|---|
| 部署路径 | publicPath: '/app/' | base: '/app/' |
| 端口 | devServer.port: 3000 | server.port: 3000 |
| 代理 | devServer.proxy | server.proxy |
| 别名 | configureWebpack.resolve.alias | resolve.alias |
| 全局变量注入 | loaderOptions.scss.additionalData | css.preprocessorOptions.scss.additionalData |
| 环境变量前缀 | VUE_APP_ | VITE_ |
| 入口 HTML | public/index.html | 根目录 index.html |
迁移步骤
# 1. 创建 Vite 项目
npm create vite@latest my-app -- --template vue
# 2. 复制源码
cp -r old-project/src my-app/src
cp -r old-project/public my-app/public
# 3. 替换环境变量前缀
# VUE_APP_API_URL → VITE_API_URL
# 4. 替换 require() 为 import
# const img = require('./logo.png') → import img from './logo.png'
# 5. 创建 vite.config.ts,删除 vue.config.js项目结构
my-project/
├── public/
│ ├── index.html # HTML 模板(EJS 语法可用环境变量)
│ ├── favicon.ico # 网站图标
│ └── static/ # 不经 Webpack 处理的静态资源
│ └── lib/ # CDN 备份的第三方库
├── src/
│ ├── api/ # API 请求封装
│ │ ├── index.js # axios 实例配置
│ │ └── modules/ # 按模块拆分
│ │ ├── user.js
│ │ └── order.js
│ ├── assets/ # Webpack 处理的静态资源
│ │ ├── images/
│ │ └── styles/
│ │ ├── variables.scss # 全局变量
│ │ ├── mixins.scss # 全局 mixin
│ │ └── global.scss # 全局样式
│ ├── components/ # 公共组件
│ │ ├── common/ # 基础组件(按钮、弹窗等)
│ │ └── business/ # 业务组件
│ ├── views/ # 页面组件(对应路由)
│ │ ├── Home.vue
│ │ ├── Login.vue
│ │ └── user/
│ ├── router/ # 路由配置
│ │ └── index.js
│ ├── store/ # Vuex 状态管理
│ │ ├── index.js # Store 入口
│ │ └── modules/ # 模块化
│ │ ├── user.js
│ │ └── app.js
│ ├── utils/ # 工具函数
│ │ ├── request.js # axios 封装
│ │ ├── auth.js # 权限工具
│ │ └── validate.js # 表单校验规则
│ ├── mixins/ # 全局混入
│ ├── directives/ # 自定义指令
│ ├── App.vue # 根组件
│ └── main.js # 入口文件
├── tests/
│ ├── unit/ # 单元测试
│ └── e2e/ # E2E 测试
├── .env # 环境变量(所有环境)
├── .env.development # 开发环境变量
├── .env.production # 生产环境变量
├── vue.config.js # Vue CLI 配置
├── babel.config.js # Babel 配置
├── .eslintrc.js # ESLint 配置
├── .browserslistrc # 浏览器兼容目标
├── postcss.config.js # PostCSS 配置
├── tsconfig.json # TypeScript 配置(如使用 TS)
└── package.json常见问题
1. 端口被占用
// vue.config.js
module.exports = {
devServer: {
port: 3000 // 改用其他端口
}
}2. 跨域
// vue.config.js — 配置代理
module.exports = {
devServer: {
proxy: {
'/api': {
target: 'http://localhost:3000', // 后端地址
changeOrigin: true, // 修改 Host 头
pathRewrite: { '^/api': '' } // 去掉 /api 前缀
}
}
}
}3. 构建体积大
# 分析哪些包最大
npm run build -- --report// 排除大依赖,改为 CDN 引入
module.exports = {
configureWebpack: {
externals: {
vue: 'Vue',
'vue-router': 'VueRouter',
vuex: 'Vuex',
axios: 'axios',
echarts: 'echarts'
}
}
}<!-- public/index.html 中引入 CDN -->
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue-router.min.js"></script>4. 首屏加载慢
// 路由懒加载:按需加载页面代码
const Home = () => import(/* webpackChunkName: "home" */ '@/views/Home.vue')
const User = () => import(/* webpackChunkName: "user" */ '@/views/User.vue')
// 移除 prefetch(避免预加载不需要的页面)
// vue.config.js
module.exports = {
chainWebpack: config => {
config.plugins.delete('prefetch')
}
}5. Docker / WSL 热更新失效
// vue.config.js
module.exports = {
devServer: {
hot: true,
watchOptions: {
poll: 1000 // 开启轮询模式
}
}
}6. Element UI 按需引入
⚠️ 以下方案适用于 Vue 2 + Element UI。Vue 3 项目应使用 Element Plus,按需引入推荐使用
unplugin-vue-components+unplugin-auto-import,详见 Element Plus 按需引入文档。
npm install -D babel-plugin-component// babel.config.js
module.exports = {
plugins: [
['component', {
libraryName: 'element-ui',
styleLibraryName: 'theme-chalk'
}]
]
}// src/plugins/element.js
import Vue from 'vue'
import { Button, Input, Table, Message } from 'element-ui'
Vue.use(Button)
Vue.use(Input)
Vue.use(Table)
Vue.prototype.$message = Message常用命令速查
| 命令 | 说明 |
|---|---|
vue create <name> | 创建项目 |
vue add <plugin> | 添加插件 |
vue serve | 快速原型开发(需先 npm i -g @vue/cli-service-global) |
vue build | 快速原型构建(需先 npm i -g @vue/cli-service-global) |
vue inspect | 查看最终 Webpack 配置 |
vue inspect --mode production | 查看生产环境配置 |
vue inspect --rule css | 查看指定 loader |
vue ui | 图形化管理界面 |
npm run serve | 启动开发服务器 |
npm run serve -- --port 3000 | 指定端口 |
npm run build | 生产构建 |
npm run build -- --report | 构建 + 包分析 |
npm run build -- --modern | 生成现代 + 兼容两份包 |
npm run lint | ESLint 检查修复 |