Webpack迁到Vite第七节(四):全家桶移除 — 保留项、脚本与数据收尾
Webpack迁到Vite第七节四全家桶移除 — 保留项、脚本与数据收尾本篇定位依赖从 48 个精简到 40 个共移除 17 个、新增 9 个净减 8 个——留下的每个都有留下的理由。本篇梳理保留原因、脚本命令的对应关系、.eslintrc.js清理了什么以及最终的依赖数量、构建时间、配置文件体积的对比数据。思维导图保留项、脚本与数据收尾 ├── 1.3 保留的依赖包23 个— 为什么这些包没有被移除 │ ├── less autoprefixerLess 编译器 CSS 前缀 │ ├── browserslist共享给 autoprefixer plugin-legacy │ ├── vue-template-compiler仅 vue/test-utils 需要 │ └── eslint 插件链 测试工具链 ├── 1.4 脚本命令更新 │ ├── 原则核心脚本名不变serve/build/lint │ ├── serve / serve:params / build / build:params / analyze │ ├── lint / lint:fix独立于构建工具 │ └── 测试脚本vitest Playwright迁移前已配置 ├── 1.5 .eslintrc.js 清理 │ ├── import/resolver webpack → custom-alias │ └── import/extensions: offVue 生态惯例 ├── 1.6 依赖数量对比 │ ├── 48 → 40净减 8 个包 │ ├── 配置文件332 → 200 行约 -40% │ ├── 构建时间3m31s → 37s-82% │ └── npm install70s → 15s-79% └── 总结1.3 保留的依赖包23 个为什么这些包被保留包名保留原因详细说明vite新的构建工具替代 Webpack vue/cli-servicevitejs/plugin-vue2Vue 2 SFC 编译Vite 需要这个插件来解析.vue文件vitejs/plugin-legacyES5 降级双构建为旧浏览器额外产出nomodule传统产物详见第七节五vite-plugin-compression.gz 预压缩替代compression-webpack-pluginrollup-plugin-visualizer构建分析替代webpack-bundle-analyzervitest单元测试独立测试框架与构建工具无关vitest/coverage-v8测试覆盖率vitest 的覆盖率插件playwright/testE2E 测试独立测试框架与构建工具无关vue/test-utilsVue 组件测试测试 Vue 组件的工具库jsdom测试 DOM 环境vitest 的 DOM 模拟环境eslint代码检查独立于构建工具eslint-plugin-vueVue 规则ESLint 的 Vue 语法规则eslint-plugin-importImport 规则ESLint 的 import 规则vue/eslint-config-airbnbAirbnb 规则预设ESLint 规则集eslint-import-resolver-custom-aliasESLint import 别名解析替代eslint-import-resolver-webpack解析/别名和.vue扩展名lessCSS 预处理器编译器本身Vite 不内置autoprefixerCSS 浏览器前缀Vite 内置 PostCSS但 autoprefixer 需要单独安装vue-template-compiler模板编译vitest 需要Vite 构建时由 vitejs/plugin-vue2 处理core-js保留^3.38.1 → ^3.50.0 升级迁移前已有迁移后升级供 plugin-legacy 传统构建注入 polyfillregenerator-runtimeasync/await 运行时配合 vitejs/plugin-legacy 为传统构建提供 regenerator 运行时terser传统产物压缩plugin-legacy 产出的 legacy chunk 由 terser 压缩esbuild 不做 ES5 降级ioredis测试 Redis 查询测试辅助工具与构建工具无关mysql2测试数据库查询测试辅助工具与构建工具无关关键保留项说明lessautoprefixer为什么保留lessVite 内置了 Less 集成层调用的胶水代码但不是编译器本身。less包是 Less 语言的编译器负责把 Less 语法解析并编译为 CSS。Vite 没有重写这个编译器。autoprefixer完整的工具链分工如下Vite构建工具 └── 内置 PostCSS 集成自动检测 postcss.config.js └── postcss.config.js → plugins: { autoprefixer: {} } └── autoprefixernpm 包需单独安装 └── 读取 browserslist 配置确定要加哪些 CSS 前缀Vite 内置了 PostCSS 的集成管道——自动检测项目中的postcss.config.js把它接入构建流程。但 PostCSS 的具体插件如autoprefixer需要自己安装——Vite 只负责管道不打包插件。browserslist为什么保留browserslist从package.json中保留原因是它仍然是两个独立消费方的共享配置消费者用途是否还在用BabelWebpack 时代babel/preset-env根据它确定需要降级哪些 ES6 语法❌ 已移除autoprefixer根据它确定要为哪些浏览器添加 CSS 前缀-webkit-等✅ 仍在使用vitejs/plugin-legacyH5 签署端 ES5 降级plugin-legacy依赖它确定传统构建的转译目标✅ 已实施详见第七节五Babel 移除不意味着browserslist应该删除——它从来不是 Babel 独占的配置。autoprefixer通过postcss.config.js接入 Vite 的 PostCSS 管线读取browserslist生成 CSS 前缀。vitejs/plugin-legacy同样依赖它确定传统构建的转译目标。完整链路postcss.config.js → autoprefixer → browserslist → CSS 前缀 ← 现在 vitejs/plugin-legacy → Babel → browserslist → ES5 语法降级 ← 已实施见第七节五package.json中的最终配置与 Webpack 时期一致browserslist:[ 1%,last 2 versions,not ie 10]不删除的另一个原因是可控性browserslist默认值会随 npm 包版本升级而变化显式声明锁定项目意图避免某天默认值改变导致构建产物意外变化。vue-template-compiler为什么保留vue-template-compiler只有一个消费者vue/test-utils1.3.6。vitest测试运行器 → 加载测试文件 → mount(Component) ← vue/test-utils → require(vue-template-compiler).compileToFunctions() → 将 Vue 模板编译为 render 函数源码验证node_modules/vue/test-utils/dist/vue-test-utils.jsvarvueTemplateCompilerrequire(vue-template-compiler);varelvueTemplateCompiler.compileToFunctions(divtemplate slotnameslotValue/template/div);vue/test-utils在 mount 组件时需要将模板字符串编译为 render 函数——这是 Vue 2 特有的步骤Vue 3 的vue/test-utils不再需要。vue/test-utils将其声明为peerDependency由消费项目提供。与vitejs/plugin-vue2无关vitejs/plugin-vue2的 peerDeps 只有vite和vue不依赖vue-template-compiler——它使用自己的内部编译器处理.vue文件。生产构建不需要vue-template-compiler。结论保留它的唯一原因是vue/test-utils需要。如果项目不使用vue/test-utils或升级到 Vue 3就可以移除。1.4 脚本命令更新原则核心脚本名不变脚本名是 CI/CD 流水线、Dockerfile、开发文档中的硬编码引用。迁移策略是核心脚本名serve/build/lint保持不变只替换内部调用的命令。带参数的辅助脚本在后续优化中统一为:params命名风格如serveStart→serve:params。逐命令分析npm run serveviteserve:vite为什么叫serve而不是dev这是直接从 Vue CLI 时期继承的命名。Vue CLI 使用vue-cli-service serve启动开发服务器所以脚本名叫serve。保持这个命名CI/CD 中写npm run serve的地方不需要改。vite命令不带参数时默认启动开发服务器默认端口 5173本项目在vite.config.js中配置为8080并开启host: true与strictPort: false——详见第九节 §1.5。npm run serve:paramsnpm run serve --brand_color#DC143C --custom_path/privatiza20 --tag_nameserve:params:npm run serve --brand_color#DC143C --custom_path/privatiza20 --tag_name迁移初期名为serveStart后续统一为:params命名风格。npm_config_机制详解当执行npm run serve --brand_color#DC143C时注意没有--分隔符npm 把--brand_color#DC143C当作 npm 自身的配置参数npm 设置环境变量npm_config_brand_color#DC143Cnpm 执行serve脚本即vitevite.config.js中process.env.npm_config_brand_color读取到#DC143C关键区分——npm 的两种参数传递方式实测验证方式 A: npm run serve --brand_color#DC143C ← 项目实际用法 → npm 解析 --brand_color → 设置 npm_config_brand_color 环境变量 → ✅ 方式 B: npm run serve -- --port 8080 ← 传给 vite 的 CLI 参数 → -- 后面的是给脚本的 → node/vite 的 process.argv 接收 → 不设环境变量#为什么安全实测npm run env-test --brand_color#DC143Cnpm 在 shell 解释之前解析参数#DC143C完整保留不会触发 shell 的注释行为。如果直接写vite --brand_color#DC143C不通过 npmbash 会把#后的内容当作注释截断。npm run buildvite buildbuild:vite buildvite build使用 Rollup 执行生产构建。默认输出到dist/目录产物经过代码分割、压缩、css 提取等处理。相比vue-cli-service build --no-moduleWebpack 时代使用--no-module是因为构建产物是 ES5用普通script标签加载所有浏览器都能执行。Vite 的构建产物是 ESM必须用script typemodule加载!-- Webpack 产物ES5 --scriptsrc/js/chunk-vendors.js/script← 所有浏览器都能加载!-- Vite 产物ESM --scripttypemodulesrc/assets/index-abc.js/script← 仅现代浏览器能加载script typemodule在 HTML 规范中的行为浏览器行为结果支持 ESMChrome 61正常下载、解析、执行✅不支持 ESMIE11、旧 Android WebView静默跳过此标签不下载不报错❌ 白屏这是 HTML 规范的刻意设计——不支持的浏览器不会尝试降级直接忽略。Vite 默认只生成script typemodule没有script nomodule兜底。这意味着旧浏览器用户看到的将是空白页面——与 Webpack 时代的 ES5 bundle 兼容性存在本质差异。这正是vitejs/plugin-legacy解决的问题详见 第七节五ES5 降级方案专题双构建后同时输出现代产物typemodule和传统产物nomodule旧浏览器通过script nomodule加载 ES5 版本。npm run analyzenpm run build --reportanalyze:npm run build --report--report参数通过npm_config_report环境变量传递给vite.config.js。vite.config.js中判断process.env.npm_config_report是否存在若存在则启用rollup-plugin-visualizer// vite.config.js 的 visualizer 条件插件...(process.env.npm_config_report?[visualizer({...})]:[]),注意npm run build --report会用--report参数调用vite build但vite build本身不认识--report。关键在于npm把--report转换为npm_config_report环境变量vite.config.js读取的是这个环境变量不是 CLI 参数。npm run build:params带参数的构建build:params:npm run build --brand_color#DC143C --custom_path/privatiza20 --tag_namev4.3.0--tag_namev4.3.0通过npm_config_tag_name传递给vite.config.js最终注入到import.meta.env.APP_TAGNAME编译时常量中。为什么tag_name、brand_color、custom_path同时出现项目构建时版本标识tag、品牌色和部署路径是独立配置的。build:params在同一个脚本中组合这三个参数覆盖实际部署时的完整需求。迁移初期名为build:tagName参数仅有--tag_name我是好人后续统一为:params命名风格并补齐参数。npm run lint:fixeslint --ext .js,.vue src --fixlint:eslint --ext .js,.vue src,lint:fix:eslint --ext .js,.vue src --fix逐参数拆解参数值作用--ext.js,.vue指定检查的文件扩展名。逗号分隔多个扩展名ESLint 只检查匹配这些扩展名的文件srcsrc指定检查目录。ESLint 递归扫描src/下所有匹配--ext的文件--fix—自动修复可修复的问题如缩进、分号、引号风格等。不可修复的问题如逻辑错误只报告不修复为什么从vue-cli-service lint --fix改为eslint --ext .js,.vue src --fixvue-cli-service lint是vue/cli-plugin-eslint提供的命令它封装了 ESLint 的调用。移除 Vue CLI 后直接使用eslint命令。vue/cli-plugin-eslint的 lint 命令做了什么额外的事情它实际上做了三件事调用 ESLint API 检查代码如果 lint 失败阻止 Webpack 编译开发环境在vue-cli-service build时lint 错误会阻止构建Vite 不集成 ESLint所以这些阻止构建的行为不再需要。ESLint 作为独立工具运行不影响构建流程。lintvslint:fix的区别lint只检查不修复用于 CI 流水线lint:fix检查并自动修复用于本地开发。两者共用同一套参数只是--fix的有无。测试脚本迁移前已配置保留不变以下 6 个测试脚本自 2026-08-13 起已配置迁移过程中保留不变test:vitest run,test:watch:vitest,test:e2e:playwright test,test:e2e:headed:playwright test --headed,test:e2e:ui:playwright test --ui,test:coverage:vitest run --coverage脚本命令作用testvitest run运行单元测试单次不监听test:watchvitest运行单元测试监听模式文件变更自动重跑test:e2eplaywright test运行 E2E 测试无头浏览器test:e2e:headedplaywright test --headed运行 E2E 测试有头浏览器弹出可见窗口查看页面实际操作test:e2e:uiplaywright test --ui打开 Playwright 交互式测试面板可浏览用例列表、按需重跑、开启时间旅行调试test:coveragevitest run --coverage运行单元测试并生成覆盖率报告--headedvs--ui的区别--headed--ui本质让浏览器窗口可见Playwright交互式测试面板浏览器窗口弹出可见窗口默认 headless可在面板中手动切换交互性无跑完即结束有——点击重跑、查看 Trace、按用例筛选适用场景调试某个测试想看页面实际操作开发阶段持续跑测试、浏览用例列表、复盘失败--headed只是把浏览器露出来——和执行普通命令的唯一区别是能看到画面。--ui打开的是 VS Code 风格的完整测试管理面板是开发阶段的主力工具。为什么是 vitest Playwright 这套组合迁移过程本身需要验证——每次改动后需要运行测试确认没有回归。vitest 与 Vite 共享同一套配置vite.config.js中的resolve.alias、css.preprocessorOptions等无需额外配置测试环境。Playwright 则独立于构建工具通过playwright/test包提供。1.5.eslintrc.js清理移除的配置原settings[import/resolver]中配置的是webpackresolver指向已不存在的vue/cli-service配置文件。该 resolver 已替换为custom-alias见下文为什么移除后还能正常工作// .eslintrc.js — webpack resolver已移除替换为 custom-aliassettings:{import/resolver:{webpack:{// ← 这个 resolver 被移除config:node_modules/vue/cli-service/webpack.config.js}}}当前.eslintrc.js中settings块保留内容改为settings:{import/resolver:{custom-alias:{// ← 替换为 custom-aliasalias:{:./src},extensions:[.js,.vue,.json,.less,.png,.svg],},},},逐行拆解settingsESLint 配置中的顶层键用于给插件传递共享配置。与rules控制规则开关不同settings是给插件内部使用的数据不直接影响 lint 规则。import/resolvereslint-plugin-import的配置项。import/前缀表示这是eslint-plugin-import插件的专属配置。resolver告诉插件如何解析 import 路径。webpack指定使用eslint-import-resolver-webpack这个 resolver。ESLint 插件系统支持多种 resolver——node默认只解析node_modules、webpack读取 webpack 配置解析别名、typescript读取 tsconfig 解析路径映射等。config: node_modules/vue/cli-service/webpack.config.js指定 webpack 配置文件的路径。resolver 会读取这个文件从中提取resolve.alias配置从而理解/→src/的映射。完整工作流程ESLint 检查 import 语句时 import { getList } from /api/user ↑ 路径别名 / 需要解析 ① 检查 settings[import/resolver] ② 发现配置了 webpack resolver ③ 读取 node_modules/vue/cli-service/webpack.config.js ④ 找到 resolve.alias: { : src/ } ⑤ 把 /api/user 解析为 src/api/user ⑥ 检查 src/api/user 文件是否存在 → 存在 → 通过为什么移除后还能正常工作两个原因Webpack 配置文件不存在了node_modules/vue/cli-service/webpack.config.js在移除vue/cli-service后已经不存在这个配置指向了一个不存在的文件。eslint-import-resolver-custom-alias替代通过alias: { : ./src }和extensions: [.js, .vue, ...]配置提供/别名和.vue扩展名解析使import/no-unresolved规则恢复到正常工作状态。为什么import/extensions: off必须保持.eslintrc.js中import/extensions: off关闭了一条规则。Airbnb 默认对其是启用的// eslint-config-airbnb-base 的默认配置import/extensions:[error,ignorePackages,{js:never,// import .js 文件时禁止写 .js 后缀mjs:never,jsx:never,}]这条规则的含义场景规则行为示例.js/.jsx文件禁止写扩展名import util from ./util✅ /import util from ./util.js❌npm 包ignorePackages不检查import Vue from vue✅未列出的扩展名含.vue要求必须写import X from ./X❌ 必须写成import X from ./X.vue如果开启项目中所有不带.vue后缀的 import 全部报错import CanvasSignature from ./CanvasSignature ❌ Missing file extension vue import SealCard from ./sealCard ❌ Missing file extension vueVue 社区的约定俗成是从来不写.vue后缀——这不是项目的个别选择而是整个 Vue 生态的惯例。Airbnb 规则不了解.vue的存在eslint-plugin-vue也不处理import/extensions。因此在 Vue 项目中这条规则必须off——与 Webpack 迁移到 Vite 无关在 Webpack 时期就应该关闭。1.6 依赖数量对比精确对比迁移前Webpack 时期分类包名数量构建核心webpack,vue/cli-service,vue/cli-plugin-babel,vue/cli-plugin-eslint4Babel 相关babel/core,babel/preset-env,babel/plugin-transform-runtime,babel-plugin-import4Loader/Pluginless-loader,style-resources-loader,svg-sprite-loader,compression-webpack-plugin,copy-webpack-plugin,git-revision-webpack-plugin6其他webpack-bundle-analyzer,eslint-import-resolver-webpack2后续替换lodash→lodash-es1 换 1迁移后独立提交1保留所有保留的 31 个包含core-js保留项^3.38.1 → ^3.50.0 升级31总计48迁移后Vite 时期分类包名数量构建工具vite,vitejs/plugin-vue2,vite-plugin-compression,rollup-plugin-visualizer4ES5 降级vitejs/plugin-legacy,core-js,regenerator-runtime,terser4测试vitest,vitest/coverage-v8,playwright/test,vue/test-utils,jsdom5Linteslint,eslint-plugin-vue,eslint-plugin-import,vue/eslint-config-airbnb,eslint-import-resolver-custom-alias5样式less,autoprefixer2编译vue-template-compiler1辅助ioredis,mysql22运行时依赖vue,vuex,axios等 17 个迁移前后不变lodash-es替换lodash17总计40对比48 → 40。共移除 17 个、新增 9 个净减 8 个迁移提交移除 16 个 后续 lodash → lodash-es 1 换 1 替换。配置文件体积对比文件迁移前迁移后减少构建配置vue.config.js310 行vite.config.js200 行35%转译配置babel.config.js22 行无100%合计332 行200 行约 40%这些减少意味着什么npm install 时间--prefer-offline实测Webpack 项目 ~70sVite 项目 ~15s减少 79%。这是因为净少装 8 个包及其传递依赖依赖树更浅Vite 生态的包体积更小esbuild 是 Go 写的发布为原生二进制文件没有 JS 依赖链node_modules 体积估计减少 40-50%。Babel 及其插件生态babel/core、babel/preset-env、babel/plugin-transform-runtime等的传递依赖非常多仅babel/core就有 20 个传递依赖。移除 Babel 后node_modules 中减少约 100 个文件。构建速度3 轮实测平均值Webpack优化项目Vite当前项目第 1 轮3m 37s35s第 2 轮3m 24s38s第 3 轮3m 31s38s平均~3m 31s (211s)~37sVite 构建速度快约5.7 倍减少 82%。差距远超esbuild 比 Babel 快 10-20 倍所能解释的范围——Webpack 的大部分时间消耗在模块依赖图构建和打包上JS 实现而 Vite 开发模式根本不打包生产模式的 Rollup 打包效率也远高于 Webpack。总结维度Webpack 时期Vite 时期配置文件vue.config.js(310行) babel.config.js(22行)vite.config.js(197行)构建核心webpack 80 个 loader/pluginVite esbuild Rollup转译BabelJS 实现慢esbuildGo 实现快 10-20 倍打包WebpackJS 实现RollupJS 实现tree-shaking 更优压缩terserJS 实现esbuildGo 实现CSS 变量注入style-resources-loaderadditionalDataSVG 图标svg-sprite-loadervirtual module 插件virtual:svg-icons文件复制copy-webpack-pluginconfigureServer closeBundle 钩子Git 版本git-revision-webpack-pluginexecSync(git rev-parse)构建分析webpack-bundle-analyzerrollup-plugin-visualizer依赖数量48 个40 个构建时间~3m 31s~37snpm install~70s~15s核心代价去掉了手动子路径导入的样式按需加载能力改为antd.less全量样式文件替代。但 Rollup 的 tree-shaking 对 JS 代码的处理效果与子路径导入一致实验验证 bundle 大小几乎相同。eslint-import-resolver-webpack替换为eslint-import-resolver-custom-alias已安装通过alias和extensions配置恢复了import/no-unresolved的完整功能。核心理念Vite 的设计哲学是内置常见功能减少配置。共移除 17 个包、新增 9 个净减 8 个——不是因为它们的功能消失了而是因为 Vite 把这些功能内置到了自己的代码中或者用更简单的方式execSync、additionalData、closeBundle钩子替代了。本节小结迁移的核心指标——依赖 48 → 40净减 8配置文件 332 → 200 行约 -40%构建 3m31s → 37s-82%npm install 70s → 15s-79%。代码量减少靠的是壳的消除vue.config.js不再是 Webpack 配置的中间层速度提升靠的是活的减少目标浏览器升级消除了转译需求。保留的 23 个包各有各的理由——less 编译器是必需品、autoprefixer browserslist 是共享配置、vue-template-compiler 是测试依赖——没有一个是为了保留而保留。