从webpack到vite——配置与特性全面对比

前言

本文将从webpack出发,探索其配置和特性在vite中的对应配置,为webpack迁移到vite提供参考,因此不过多介绍webpack中该配置项的作用。本文比对的配置项涵盖webpack中的绝大部分配置项,但也会忽略部分极少用到的配置。

webpack打包的目标环境,既支持浏览器环境,也支持node环境,而vite整体上以web环境优先。实际开发中,如果打包的目标环境以node为主,并不建议从webpack迁移vite。因此本文对两者配置项或特性的对比,也主要考虑web环境下的差异。

webpack配置/特性在vite中的对应

mode和环境变量

webpack vite 说明
mode mode 效果基本一致,vite无需手动设置
DefinePlugin .env文件 暴露对象不一致(process.envimport.meta.env

mode

webpack和vite都有mode字段,设置的值为developmentproduction,作用基本一致。区别主要是webpack需要手动设置,vite由dev和build命令自动设置。

环境变量

两者都自动将mode的值设置到process.env.NODE_ENV,其他环境变量webpack通过DefinePlugin设置到process.env上,而vite通过.env文件设置到import.meta.env

构建目标环境(target)和兼容性

webpack vite 说明
target build.target 默认值不一致

webpack中默认是browserslist,且支持node环境,而vite默认是baseline-widely-available(['chrome107', 'edge107', 'firefox104', 'safari16']),可以设置为合法的esbuild的target选项esbuild.github.io/api/#target

入口entry和多入口打包

webpack vite 说明
entry index.html中指定 多入口需要通过rollup配置

vite没有webpack的entry配置,对应的是index.html中通过script的src指定入口文件(库打包模式下在build.lib中指定),如果是多入口,则需要通过rollup进行配置

css 复制代码
 rollupOptions: {  
      input: {  
        main: resolve(__dirname, 'index.html'),  
        admin: resolve(__dirname, 'admin.html')  
      }  
    }

vite自动处理多入口共享依赖(不需要类似webpack配置dependOn)`

输出output

webpack vite 说明
publicPath base
path build.outDir
hash文件名 默认不需要配置 vite可通过 rollupOptions.output 设置文件名
clean 内置
hash生成算法 仅支持hash长度配置
iife 自身无对应配置 可参考库打包中的iife模式
importFunctionName 不支持 一般不需要设置
library 基本覆盖 见下文见库打包模式
pathinfo 不支持 source-map已覆盖该需求
wasmLoading 不支持 见下文 worker和wasm
workerChunkLoading 不支持 见下文 worker和wasm

对一些配置项的特殊说明

  • filename/chunkFilename/assetModuleFilename: 对应vite的rollupOptions.output中的entryFileNames/chunkFileNames/assetFileNames,vite一般不需要配置

  • clean:对应vite的build.emptyOutDir,vite默认清理,不需要设置

  • auxiliaryComment: 类似rollup中的banner

  • globalObject: vite自动判断,不需要设置

  • hash: webpack相关配置或插件有hashDigest/hashDigestLength/hashFunction/hashSalt/webpack.ids.HashedModuleIdsPlugin,但一般不需要配置。vite中可以使用[hash:8]形式调整长度,不支持调整算法

  • importFunctionName: webpack可以调整import的名字,主要为了使用dynamic import的polyfill时可能会用到。目前dynamic import已经被广泛支持了,该配置项已过期。

  • pathinfo:webpack能够对代码添加类似/*! ./src/utils.js */的路径信息注释, 方便调试,vite中无法配置,但该需求实际上被source-map覆盖,也无需配置

devServer

webpack vite 说明
devServer server
devServer.port server.port
devServer.open server.open
devServer.allowedHosts server.allowedHosts
devServer.proxy server.proxy 除pathRewrite配置不一样以及不支持compress外,其他基本一致
devServer.server server.https
devServer.watchFiles server.watch 不要监听node_modules目录下文件
devServer.compress
devServer.bonjour 开发环境一般都能ip访问,该配置使用较少
devServer.client logLevel + server.hmr vite不支持progress,该配置也比较鸡肋
devServer.liveReload 该配置和热更新冲突,一般使用热更新而关闭该配置
devServer.hot vite默认热更新,无需配置
devServer.static public目录

mock

webpack一般通过devserver的setupMiddlewares配置mock,vite有多个mock相关插件,如vite-plugin-mock-dev-server

热更新

webpack vite 说明
配置复杂 开箱即用
按chunk编译 文件级按需编译 vite启动快,加载慢;webpack启动慢,加载快

webpack仅提供了热更新的能力,但热更新的具体行为,还是需要通过配置实现。

比如React项目热更新,webpack需要额外配置react-refresh@pmmmwh/react-refresh-webpack-plugin ,vite中由@vitejs/plugin-react提供热更新能力,无需额外配置。

库打包模式

webpack vite 说明
library lib + rollupOptions 支持目标格式较webpack少,但基本能满足需求

webpack相关配置有library/libraryTarget(library.type)/libraryExport(library.export),对应vite库打包模式,build配置项demo如下

javascript 复制代码
 lib: {
      entry: 'src/index.js',    // 需要指定库入口文件
      name: 'MyLibrary',        // 对应 Webpack 的 library.name
      formats: ['umd', 'es'],   // 对应 libraryTarget(支持多格式)
      fileName: (format) => `my-lib.${format}.js`
    },
    rollupOptions: {
      output: {
        exports: 'default',     // 对应 libraryExport: 'default'
        external: ['vue'],
        globals: {              // 外部依赖的全局变量映射(如 Vue -> window.Vue)
          vue: 'Vue'
        }
      }
    }

webpack的libraryTarget考虑多种环境(浏览器/worker/node)的兼容问题,因此有各种细分类型: assign/assign-properties/commjs2/var/this/global/self等,rollup针对现代标准规范,支持'es' | 'cjs' | 'umd' | 'iife',基本满足需求,对assign等特殊场景,需要通过自定义插件实现

cache

webpack vite 说明
cache 支持 默认开启无需配置

webpack中cache默认是内存缓存,一般需要手动设置typefilesystem,可设置缓存的保存目录/压缩/过期/间隔等,对应vite中的cacheDir,一般不需要配置

resolve

webpack vite 说明
resolve resolve
resolve.alias resolve.alias 一致
resolve.mainFields resolve.mainFields 一致
resolve.extensions resolve.extensions 一致
resolve.symlinks resolve.preserveSymlinks 默认值相反,效果一致
resolve.modules 不支持 webpack中极少配置
resolve.mainFiles 不支持 webpack中极少配置

特殊说明

以下配置项webpack与vite基本一致

  • alias: 路径别名,如'@/pages'
  • mainFields: package.json入口字段优先级
  • extensions:按顺序解析文件后缀名

以下配置项存在差异:

  • symlinks: webpack默认值为true,vite对应配置项为preserveSymlinks,默认false,效果一致

其余配置项,基本用于如何确定import引入对象文件,webpack中一般采用默认值,vite并无对应,但也无需配置,比如

  • modules: 模块查找目录,默认['node_modules'],webpack一般不需要配置,vite无该配置
  • mainFiles:模块入口文件,默认['index'],webpack一般不需要配置,vite无该配置

文件解析编译

webpack vite 说明
js/ts babel esbuild vite默认支持,无需配置
jsx/tsx @babel/preset-react @vitejs/plugin-react
ts类型检查 fork-ts-checker-webpack-plugin vite-plugin-checker vite默认不处理,由IDE检查
eslint eslint-webpack-plugin vite-plugin-eslint
css css-loader/style-loader/mini-css-extract-plugin css vite默认支持,无需配置
less/sass less-loader/sass-loader css.preprocessorOptions
postcss postcss-loader css.postcss

j(t)s(x)文件解析和编译

vite中对jsx/tsx的解析,需要额外使用@vitejs/plugin-react,对js/ts的解析默认不需要配置。具体差异见babel/terser vs esbuild

样式文件编译抽离

webpack需要配置css-loader,less-loader等loader进行样式文件解析,通过style-loader注入style标签,mini-css-extract-plugin抽离为css文件以link引入,vite中无需配置,开发环境将样式注入style标签,生产环境则抽离为css文件。

webpack可通过loader选项进行精细化控制,vite对应的是css配置项:

  • module: postcss-modules
  • postcss: 格式同postcss.config.js,相当于webpack的postcss-loader
  • preprocessorOptions: 对不同预处理器进行配置,类似less-loader/sass-loader中的选项
  • preprocessorMaxWorkers:CSS 预处理器可以使用的最大线程数,类似webpack中的thread-loader
  • devSourcemap:启用sourcemap,对应webpack css-loader中的sourcemap配置

此外vite可以通过transformer配置项,设置lightningcss 以代替postcss,处理速度更快,不过生态不如postcss,目前不建议。

vite 生产打包时的额外可配置项如下:

  • cssCodeSplit:css拆分,相当于webpack中的mini-css-extract-plugin
  • cssTarget: css兼容性,webpack中通过postcss-loader + postcss-preset-env实现
  • cssMinify: 可以指定esbuild还是lightningcss压缩,webpack中采用mini-css-extract-plugin
  • sourcemap:同上devSourcemap

静态文件处理

webpack vite 说明
普通静态文件asset asset module assetsDir + assetsInlineLimit 功能基本一致,vite配置简单
json5 json5-loader vite-plugin-json5 普通json都可以直接import
svg @svgr/webpack vite-plugin-svgr

asset

webpack5通过asset module处理,

  • asset/resource 发送一个单独的文件并导出 URL。之前通过使用 file-loader 实现。
  • asset/inline 导出一个资源的 data URI。之前通过使用 url-loader 实现。
  • asset/source 导出资源的源代码。之前通过使用 raw-loader 实现。
  • asset 在导出一个 data URI 和发送一个单独的文件之间自动选择。之前通过使用 url-loader,并且配置资源体积限制实现。

vite中配置项只有两个

  • assetsDir: 静态文件目录,webpack中通过assetModuleFilename实现
  • assetsInlineLimit:相当于webpack的asset中设置dataUrlCondition

JSON

webpack和vite都可以直接引入json文件,但如果需要在json中添加注释,webpack需要配置json5-loader,vite对应插件为vite-plugin-json5

svg引用

webpack中可以使用@svgr/webpack将svg转为组件,对应vite插件vite-plugin-svgr,import url上需要额外加查询参数reactimport Logo from "./logo.svg?react";

webpack常用插件在vite中的对应配置

webpack vite 说明
terser-webpack-plugin 内置默认esbuild
css-minimizer-webpack-plugin 内置默认esbuild
html-webpack-plugin vite-plugin-html
fork-ts-checker-webpack-plugin
eslint-webpack-plugin vite-plugin-eslint
webpack-manifest-plugin build.manifest
copy-webpack-plugin vite-plugin-static-copy 默认复制public目录下的内容
code-inspector-plugin code-inspector-plugin
webpack-bundle-analyzer 本身不支持 可通过roll配置rollup-plugin-visualizer
case-sensitive-paths-webpack-plugin 本身不支持 可通过自定义vite plugin实现

html模板编译

webpack中的html-webpack-plugin对应vite-plugin-html,但vite中可以向html中注入环境变量,一般情况也不需要配置该插件

ts类型检查

webpack通常用fork-ts-checker-webpack-plugin进行类型检查,vite默认不做类型检查,检查功能由IDE提供,可以通过插件vite-plugin-checker实现类似效果

eslint

webpack通过eslint-webpack-plugin配置,vite对应的是vite-plugin-eslint,eslint配置.eslintrc.js内容一致

manifest

webpack中的webpack-manifest-plugin对应vite中的build.manifest

文件复制

webpack中的copy-webpack-plugin对应到vite中可以使用vite-plugin-static-copy,但默认下,vite会将public目录下的文件复制到构建产物根目录中( copyPublicDir)

code-inspector-plugin

code-inspector-plugin同时支持webpack和vite

bundle分析

webpack-bundle-analyzer,vite默认提供了build.reportCompressedSize,需要分析bundle,可以配置rollup-plugin-visualizer

大小写敏感

webpack中的case-sensitive-paths-webpack-plugin,vite没有对应插件,需要自行编写插件实现

Optimization优化压缩与分包

webpack vite 说明
minimize/minimizer build.minify vite默认使用esbuild压缩,而webpack一般配置css-minimizer-webpack-plugin和terser-webpack-plugin
sideEffects rollup与webpack对sideEffect的判断不一样,见下文 tree shaking 和 sideEffects
splitChunks vite依赖rollup的manualChunks,配置能力较弱

webpack常用的配置项(minimize、minimizer、splitChunks、sideEffects),vite或rollup 都有对应的配置,其他比如chunkId等等,vite无对应配置,不过这些配置项在webpack中也极少用到。

这里最显著的差异是压缩插件,webpack大多数都是配置terser和css-minimizer-webpack-plugin,而vite默认esbuild,下文会对此作更具体比较。

另一个需要注意的点是分包。rollup的manualChunks的配置仅相当于webpack的splitChunks.cacheGroups的部分功能,对于运行时访问速度较高的C端项目,以及一些超大型项目,可能需要webpack的splitChunks的能力,如果仍然想用vite,可以尝试rolldown。

devtool和sourcemap

webpack vite 说明
devtool build.sourcemap vite可选类型较少

webpack的devtool对应vite的build.sourcemap,vite只有true/false/inline/hidden, true相当于webpack的source-map,vite配置虽少,但可以满足开发和生产需求

babel/terser vs esbuild

esbuild优势主要在性能上,虽然同时也具备了打包(转义+压缩)能力,但是生态与覆盖场景较babel少很多。

babel vs esbuild

esbuild相比babel,需要关注的点是它的转译能力:

情况 Babel esbuild
新语法(如 ?.、??、class fields) ✔️ 转换成旧语法等价物 ✔️ 也能转换,但仅限标准语法
实验性语法(如 decorators) ✔️ 有插件支持 见下文装饰器问题
运行时 API(如 Promise、Map) ✔️ 可自动注入 polyfill(core-js) ❌ 不注入,需外部手动引 polyfill
async/await ✔️ 转换为 generator + regenerator-runtime ✔️ 转换为 Promise 链,但不提供 Promise polyfill

如果要兼容旧浏览器,比如IE11,esbuild需要自行额外引入polyfill,而babel则通过@babel/preset-env自动注入。

perl 复制代码
{  
    "presets": [  
        [  
            "@babel/preset-env",  
            {  
                "useBuiltIns": "entry",  
                "corejs": "3.22"  
            }  
        ]  
    ]  
}

常用babel插件与vite/esbuild对照

babel vite/esbuild 说明
@babel/preset-react @vitejs/plugin-react @vitejs/plugin-react包括了jsx编译和热更新
@babel/preset-env esbuild 不支持自动polyfill
@babel/preset-typescript esbuild 不支持d.ts文件生成
babel-plugin-transform-typescript-metadata 不支持 详见下文装饰器问题
@babel/plugin-proposal-decorators esbuild 详见下文装饰器问题
@babel/plugin-proposal-class-properties esbuild 现代浏览器和 esbuild 已原生支持类属性语法,无需额外配置。若需兼容旧浏览器,通过 build.target 控制降级
transform-react-remove-prop-types 不支持 生产构建时移除 React 组件的 PropTypes 检查代码,减小体积,vite不支持

对esbuild或vite不支持的配置,可以采用以下方式使用babel插件(使用babel插件会影响打包速度)

css 复制代码
import react from '@vitejs/plugin-react';

export default defineConfig({
  plugins: [
    react({
      babel: {
        plugins: [
          process.env.NODE_ENV === 'production' && 'transform-react-remove-prop-types'
        ].filter(Boolean)
      }
    })
  ]
});

terser vs esbuild

terser常用特性对比esbuild如下:

terser esbuild 说明
target ecma target terser 支持解析/压缩/输出各自设定target
name保留 keep_classnames/keep_fnames keepNames
删除console和debugger drop_console/drop_debugger drop
压缩 针对不同case的丰富的配置项 minify esbuild配置简单,但粒度较粗
混淆 mangle mangleProps支持正则匹配 esbuild远弱于terser
tree shaking 不支持,但可以删除dead code 支持 terser由bundler处理tree shaking
兼容性 esbuild目标是现代浏览器,不具备类似terser的safari10/ie8配置项

从功能上来说,无论是配置能力,还是兼容性,terser完胜esbuild;在压缩体积上,由于terser更精细化的配置,其结果也比esbuild略小。

不过只要不考虑兼容性,对大多数项目来说,esbuild的性能收益要远大于功能和体积上的代价了。

装饰器问题

esbuild能转换当前的装饰器提案(stage3)和ts的旧版装饰器(stage1)语法,如果在tsconfig中配置了experimentalDecorators,则按照ts的旧版装饰器语法进行转译。

esbuild不支持emitDecoratorMetadata。因此如果代码存在元数据编程,不要使用esbuild。

装饰器如果使用了反射,必须保留类名,因此在压缩上需要额外配置 keep names。

装饰器历史问题

现行主流装饰器语法有两套:当前es中的装饰器(stage3)与ts 对stage1装饰器的实现(tsconfig中的experimentalDecorators)。

babel编译插件@babel/plugin-proposal-decorators的legacy模式 ,针对的不是ts的装饰器的实现,而是babel自己对es装饰器规范stage1的实现。

其区别体现在:ts属性装饰器,接收两个参数,且不需要返回属性描述符,而babel的@babel/plugin-proposal-decorators 的legacy,属性装饰器接收三个参数,第三个参数为属性描述符,且必须返回属性描述符。

vite中esbuild对装饰器的解析,默认会根据tsconfig中experimentalDecorators字段处理,为true时,是对ts的装饰器stage1的实现进行的转换。

tree shaking 和 sideEffects

sideEffects字段并不是package.json中的标准字段,但webpack的tree shaking依赖该字段,因此大多数npm包也会声明sideEffects。vite生产环境由rollup打包,它主要依赖于静态分析代码的导入和导出,本身并不处理sideEffects字段,但可能有部分插件会处理该字段。

vite的devserver真的比webpack更快吗

一般情况下,vite的开发服务器启动速度确实要比webpack更快,然而对一个超大型项目(或超大型ui库)而言,vite的esm按需加载机制,可能在首屏加载数千乃至数万个文件,这会显著增加页面加载时间,甚至有可能超过webpack的bundle时间。

vite对此做了至少两种优化:

  1. 预构建node_modules中的依赖文件,用esbuild将其打包为esm;
  2. server.warmup 预热常用文件

因此对于单个大型项目来说,要尽可能考虑将通用模块或独立的聚合业务模块封装为npm包,而超大型项目,应该向底座化或平台化方向发展,以微前端或微模块做内容加载。

worker和wasm

这两个单独列出来,是因为两者原生的文件引入方式和在bundler中的引入方式不一样------在webpack和vite中如果按照原生写法,打包后会报错404。

在bundler中,通用的写法是通过new Url(path,import.meta.url),webpack5和vite都会将这个path打包为单个chunk,并将path编译为引用路径

worker

原生

ini 复制代码
 const worker = new Worker('./worker.js');

webpack

arduino 复制代码
const worker = new Worker(new URL('./worker.js', import.meta.url));

webpack4中可以使用worker-loader或多入口打包,将worker单独打包为一个chunk

vite

arduino 复制代码
const worker = new Worker(new URL('./worker.js', import.meta.url))

javascript 复制代码
import MyWorker from './worker?worker'

const worker = new MyWorker()

wasm

原生

javascript 复制代码
fetch('./module.wasm').then((response) => response.arrayBuffer()).then((bytes) => WebAssembly.instantiate(bytes)).then((result) => {});

webpack

javascript 复制代码
import('./module.wasm').then((wasmModule) => { wasmModule.doSomething(); });

vite

javascript 复制代码
import init from './example.wasm?init'

init().then((instance) => {
  instance.exports.test()
})

javascript 复制代码
import wasmUrl from 'foo.wasm?url'

const main = async () => {
  const responsePromise = fetch(wasmUrl)
  const { module, instance } =
    await WebAssembly.instantiateStreaming(responsePromise)
  /* ... */
}

main()

模块联邦

vite本身不支持,但可以尝试@module-federation/vite(有不少bug)。

这里多说一句,在实践中,远程组件的最佳技术方案不是模块联邦,而是微模块。和微前端一样,微模块的实施并不是单个技术(比如hel-micro)的实践,而是应作为公司前端基础设施平台之一进行建设。

结论

如果没有兼容性负担,没有使用模块联邦,并且项目中也没有深度实践元数据编程,可以从webpack安全地迁移到vite。

相关推荐
程序员鱼皮2 小时前
我代表编程导航,向大家道歉!
前端·后端·程序员
车前端2 小时前
极致灵活:如何用一个输入框,满足后台千变万化的需求
前端
用户11481867894842 小时前
Rollup构建JavaScript核验库,并发布到NPM
前端
肥晨2 小时前
前端私有化变量还只会加前缀嘛?保姆级教程教你4种私有化变量方法
前端·javascript
小高0072 小时前
前端 Class 不是花架子!3 个大厂常用场景,告诉你它有多实用
前端·javascript·面试
不喝奶茶哦喝奶茶长胖2 小时前
CSS 文本换行控制:text-wrap、white-space 和 word-break 详解
前端
傅里叶2 小时前
Flutter用户体验之01-避免在 build() 或 initState() 内直接做耗时 blocking
前端·flutter
namehu2 小时前
搞定 iOS App 测试包分发,也就这么简单!😎
前端·ios·app
code_YuJun2 小时前
1. 使用VueCli编译生产环境代码以及创建不同模式
前端