文章目录
- 概要
- [1. Vue CLI创建项目](#1. Vue CLI创建项目)
- [2. 减小打包体积](#2. 减小打包体积)
- [3. 优化打包速度](#3. 优化打包速度)
- [4. 优化 FCP/LCP](#4. 优化 FCP/LCP)
- [5. 完整配置方案](#5. 完整配置方案)
- 小结
概要
当技术舞台的聚光灯已聚焦于Vite等新兴工具,Vue CLI似乎成了一位沉默的"老将"。然而,在2026年的门槛前,我们有必要摘下"过时"的滤镜,重新审视它的价值。这种重新认识,并非倡导在新项目中回归传统,而是基于一个朴素的现实:技术的生命力不仅在于创新,也在于对存量世界的深刻理解。
大量运行中的企业级应用仍由Vue CLI构建,它们是业务收入的基石。这意味着,精通Vue CLI不再是选择题,而是成为前端开发者应对复杂维护、增量迁移乃至团队协作的关键素养。理解它的配置逻辑与项目结构,无异于掌握了一把开启"技术遗产"大门的钥匙。这并非怀旧,而是一种务实的专业主义------让我们在拥抱未来的同时,也能稳健地支撑起过去。
1. Vue CLI创建项目
-
安装 Node.js (v16+),建议先安装 nvm-windows
bashnode -v # 应输出 v16.x 或更高 npm -v # 应输出 8.x 或更高 npm config set registry https://registry.npmmirror.com/ # 设置淘宝镜像源 npm config get registry # 输出 https://registry.npmmirror.com/ -
安装 Vue CLI 脚手架
bashnpm install -g @vue/cli vue --version # 应输出 v5.x.x(最新版) -
Vue CLI 创建项目
bash# 命令行创建 vue create my-vue-app # 按需选择 # 可视化创建 vue ui # 自动打开浏览器界面创建项目 -
Vue 项目结构
bashmy-vue-app/ ├── public/ # 静态资源(直接复制到输出目录) ├── src/ │ ├── assets/ # 图片/字体等资源 │ ├── components/ # 可复用组件 │ ├── router/ # 路由配置(index.js) │ ├── store/ # Vuex 状态管理(index.js) │ ├── views/ # 页面级组件 │ ├── App.vue # 根组件 │ └── main.js # 应用入口(初始化Vue实例) ├── .eslintrc.js # ESLint 代码规范配置 └── vue.config.js # Webpack 自定义配置(需手动创建) -
vue.config.js
Vue CLI 5 隐藏了复杂配置(如 webpack),可通过 vue.config.js 覆盖。jsmodule.exports = { devServer: { port: 8080, // 修改开发服务器端口 open: true // 启动后自动打开浏览器 }, lintOnSave: false // 关闭保存时代码检查(根据需求开启) } -
启动开发服务器
bashnpm run serve # 访问 http://localhost:8080 -
添加新功能插件
bashvue add @vue/cli-plugin-eslint # 添加ESLint插件 vue add vuetify # 添加UI框架 -
生产环境打包
bashnpm run build # 生成 dist/ 目录(部署文件)
2. 减小打包体积
Vue CLI 项目中,JS 代码压缩和 CSS 代码压缩 功能已内置 支持且默认开启 ,无需额外配置 。Vue CLI内部使用Webpack,而Webpack在生产模式下(mode: 'production')默认会启用TerserPlugin(用于JS压缩)和CssMinimizerPlugin(用于CSS压缩)。Vue CLI的默认配置已经将mode设置为production,因此压缩是自动开启的,无需额外配置。
Vue CLI 内部默认支持 JS 和 CSS 的代码分割 ,无需额外配置。JS 分割通过 Webpack 的 SplitChunksPlugin 和动态导入实现。CSS 分割通过 mini-css-extract-plugin 实现。代码分割只在生产构建时生效。
-
代码分割与懒加载
使用动态导入分割路由组件和第三方库:
js// 路由配置 const UserProfile = () => import('@/views/UserProfile.vue') // 按需加载 -
Tree Shaking
Vue CLI 已内置了 Tree Shaking 优化机制。需要手动干预的情况:
- 第三方库不支持 Tree Shaking
- CSS Tree Shaking
js// vue.config.js import purgecss from '@fullhuman/postcss-purgecss'; // vue.config.js module.exports = { // CSS Tree Shaking // 通过 @fullhuman/postcss-purgecss 插件移除未使用的 CSS // 仅在生产环境中启用 css: { loaderOptions: { postcss: { plugins: [ purgecss({ content: ["./src/**/*.vue", "./public/index.html"], }), ], }, }, }, // 配置 webpack 优化项 chainWebpack: (config) => { // 添加缓存组以分离特定第三方库 // 这里以 lodash 和 moment 为例 config.plugin("optimize").tap((args) => { args[0].option.splitChunks.cacheGroups.vendor = { test: /[\\/]node_modules[\\/](lodash|moment)[\\/]/, name: "vendor", chunks: "all", }; return args; }); }, }; -
压缩资源
启用 Gzip/Brotli 压缩,要安装
compression-webpack-plugin):jsimport CompressionPlugin from 'compression-webpack-plugin'; module.exports = { chainWebpack: config => { config.plugin('compression').use(CompressionPlugin, [{ algorithm: 'gzip', threshold: 10240 }]) } } -
按需引入第三方库
以 echarts 为例:
jsimport * as echarts from 'echarts/core' import { BarChart } from 'echarts/charts' import { GridComponent } from 'echarts/components' echarts.use([BarChart, GridComponent]) -
图片优化
自动转 WebP(要安装
image-webpack-loader):jschainWebpack: config => { config.module.rule('images') .use('image-webpack-loader') .loader('image-webpack-loader') }
3. 优化打包速度
-
缓存配置(Webpack 5)
jsmodule.exports = { configureWebpack: { cache: { type: 'filesystem', allowCollectingMemory: true } } } -
多线程构建
安装thread-loader:jschainWebpack: config => { config.module .rule('js') .use('thread-loader') .loader('thread-loader') } -
缩小文件搜索范围
jsconfigureWebpack: { resolve: { alias: { '@': path.resolve(__dirname, 'src') }, extensions: ['.js', '.vue'] // 减少扩展名检测 }, module: { noParse: /lodash|echarts/ // 不解析已预编译库 } } -
【传统方案】DLL 预构建
-
创建 dll.config.js:
jsimport webpack from 'webpack'; module.exports = { entry: { vendor: ['echarts', 'lodash'] }, output: { filename: '[name].dll.js', library: '[name]' }, plugins: [ new webpack.DllPlugin({ name: '[name]', path: './dll/[name]-manifest.json' }) ] } -
在 vue.config.js 中引用:
jsimport { DllReferencePlugin } from 'webpack'; module.exports = { plugins: [ new DllReferencePlugin({ manifest: require('./dll/vendor-manifest.json') }) ] }
-
-
【现代方案】使用Webpack 5的持久化缓存和代码分割来替代传统的DLL方案
Webpack 5的两个核心特性可以完美替代DLL:- 持久化缓存(cache.filesystem):通过文件系统缓存构建结果,二次构建时直接复用。
- 代码分割(splitChunks):自动分离第三方库,配合长期缓存策略。(特别要注意splitChunks的配置细节。设置chunks: 'all'和minSize: 0能确保所有第三方库都被分离,而cacheGroups可以精细控制分组策略。)
jsmodule.exports = { configureWebpack: { cache: { type: 'filesystem', buildDependencies: { config: [__filename] } } }, chainWebpack: config => { config.optimization.splitChunks({ cacheGroups: { echarts: { test: /[\\/]node_modules[\\/]echarts[\\/]/, name: 'echarts', chunks: 'all' }, lodash: { test: /[\\/]node_modules[\\/]lodash[\\/]/, name: 'lodash', chunks: 'all' } } }) } }
4. 优化 FCP/LCP
-
关键 CSS 内联
使用 critters-webpack-plugin:jsimport Critters from 'critters-webpack-plugin'; module.exports = { configureWebpack: { plugins: [new Critters()] } } -
预加载关键资源
-
预加载 (
preload): 用于当前页面中优先级高 的资源,这些资源对当前页面非常重要,需要尽快加载 。浏览器会优先加载这些资源。js// vue.config.js module.exports = { chainWebpack: config => { // 预加载关键资源(如首屏CSS/JS) config.plugin('preload').tap(options => { options[0] = { rel: 'preload', // 预加载指令 include: 'initial', // 只预加载初始路由资源 fileBlacklist: [/\.map$/, /hot-update\.js$/] // 排除不需要预加载的文件 } return options }) } } -
预取 (
prefetch): 用于未来可能使用 的资源(例如,用户可能访问的下一个页面),浏览器会在空闲时加载 这些资源,不会影响当前页面的加载。js// vue.config.js module.exports = { chainWebpack: config => { // 预取非关键资源(如异步路由组件) config.plugin('prefetch').tap(options => { options[0] = { rel: 'prefetch', // 资源预取指令 include: 'asyncChunks' // 预取所有异步chunk(自动预取所有异步加载的模块) } return options }) } }
-
-
图片懒加载
安装 vue-lazyload,配置在"5.完整配置方案":js<img v-lazy="imageUrl" alt="description"> -
服务端渲染(SSR):推荐使用Nuxt.js框架
-
CDN 加速静态资源
js// vue.config.js module.exports = { chainWebpack: config => { config.externals({ echarts: 'echarts', lodash: '_' }) } }html<!-- public/index.html --> <script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js"></script>
5. 完整配置方案
-
vue.config.js
jsimport path from 'path'; import CompressionPlugin from 'compression-webpack-plugin'; import Critters from 'critters-webpack-plugin'; import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer'; module.exports = { // 基本路径配置 publicPath: process.env.NODE_ENV === 'production' ? '/static/' : '/', // 关闭生产源映射 productionSourceMap: false, // webpack优化配置 configureWebpack: { resolve: { alias: { '@': path.resolve(__dirname, 'src') }, extensions: ['.js', '.vue'], symlinks: false }, module: { noParse: /lodash|echarts/ // 不解析预编译库 }, plugins: [ // 资源压缩 new CompressionPlugin({ algorithm: 'gzip', threshold: 10240, minRatio: 0.8 }), // 关键CSS内联 new Critters({ preload: 'swap', fonts: false }), // 打包分析 new BundleAnalyzerPlugin({ analyzerMode: process.env.ANALYZE ? 'server' : 'disabled' }) ], // Webpack 5持久化缓存 cache: { type: 'filesystem', buildDependencies: { config: [__filename] } } }, // chainWebpack高级配置 chainWebpack: config => { // 图片优化 config.module.rule('images') .use('image-webpack-loader') .loader('image-webpack-loader') .options({ bypassOnDebug: true }) // 多线程构建 config.module.rule('js') .use('thread-loader') .loader('thread-loader') .options({ workers: 3 }) // 代码分割 config.optimization.splitChunks({ chunks: 'all', minSize: 20000, maxSize: 250000, cacheGroups: { echarts: { test: /[\\/]node_modules[\\/]echarts[\\/]/, name: 'echarts', priority: 20 }, lodash: { test: /[\\/]node_modules[\\/]lodash[\\/]/, name: 'lodash', priority: 10 } } }) // CDN外部化 config.externals({ echarts: 'echarts', lodash: '_' }) } } -
HTML模板配置
html<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width,initial-scale=1.0"> <!-- 预加载关键资源 --> <link rel="preload" href="/static/js/app.1234.js" as="script"> <!-- CDN引入外部库 --> <script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js"></script> </head> <body> <div id="app"></div> <!-- 预取非关键资源 --> <link rel="prefetch" href="/static/js/chunk-vendors.5678.js"> </body> </html> -
代码优化
-
按需引入
js// utils/echarts.js import * as echarts from 'echarts/core' import { BarChart, LineChart } from 'echarts/charts' import { GridComponent, TooltipComponent } from 'echarts/components' import { CanvasRenderer } from 'echarts/renderers' echarts.use([ BarChart, LineChart, GridComponent, TooltipComponent, CanvasRenderer ]) export default echarts -
路由懒加载
js// router/index.js const Home = () => import(/* webpackChunkName: "home" */ '@/views/Home.vue') const Dashboard = () => import(/* webpackPrefetch: true */ '@/views/Dashboard.vue') -
图片懒加载
js// main.js import VueLazyload from 'vue-lazyload' Vue.use(VueLazyload, { preLoad: 1.3, attempt: 3 })
-
小结
- Vue CLI 是一个功能完整的项目脚手架系统,而 Webpack 是 Vue CLI 底层的静态模块打包器。Vue CLI 的核心价值在于它将 Webpack 等底层工具的复杂配置封装了起来,提供了一套开箱即用的标准化方案。
- 预设配置:当你使用 vue create命令创建一个新项目时,Vue CLI 已经为你生成了一套经过优化的完整 Webpack 配置,包括对 .vue单文件组件的处理、开发服务器的热重载、生产环境的代码压缩等。
- 命令行接口:Vue CLI 提供的 vue-cli-service封装了常用的脚本命令(如 npm run serve和 npm run build)。你在执行这些命令时,实际上是 @vue/cli-service在背后调用并执行配置好的 Webpack 任务。
- 可配置性:你无需执行 eject操作来暴露所有配置。只需在项目根目录创建 vue.config.js文件,就可以轻松地覆盖或扩展内部的 Webpack 配置,例如设置代理解决跨域问题。
- Vue CLI的Tree Shaking
- Vue CLI 通过预置配置简化了 90% 的 Tree Shaking 工作,开发者只需专注按需引入代码和选择支持 Tree Shaking 的依赖库即可【开箱即用】。
- Vue CLI 代码压缩
- 开箱即用
- Vue CLI 的生产构建流程默认包含 JS/CSS 压缩,参考 Webpack 的 production 模式配置
- 压缩层级
- JS 压缩:通过 terser-webpack-plugin 实现(UglifyJS 的替代品)
- CSS 压缩:通过 css-minimizer-webpack-plugin 实现
- 开箱即用
- Vue CLI 代码分割
- 开箱即用
- Vue CLI 默认启用代码分割:
- JS 分割:通过 Webpack 的 splitChunks 策略
- CSS 分割:通过 mini-css-extract-plugin 实现
- 分割触发条件
- 路由级动态导入 (
import('./Component.vue')) - 第三方依赖自动分离
- 公共模块复用检测
- 路由级动态导入 (
- 开箱即用
- 预加载关键资源和预取非关键资源
- 关键资源选择:仅预加载首屏渲染必需的资源(<500KB)
- 预取适度:避免过度预取导致带宽浪费
- HTTP/2支持:确保服务器支持多路复用