v1 -- v2
-
新特性
-
原生支持 ES6 模块
-
可以混用 ES2015 和 AMD 和 CommonJS
-
借鉴 rollup,支持 tree-shaking(减少打包后的体积)
{ "presets": [ ["es2015", {"modules": false}] } optimization: { // 模块只导出被使用的成员 usedExports: true, // 压缩输出结果 minimize: true, // 移除副作用代码 sideEffects: true }
-
-
新增更多的 CLI 参数项 -p 指定当前的编译环境为生产环境,即设置 process.env.NODE_ENV 为 production
-
差异
-
resolve 配置
-
extensions:取消了空字符串(表示导入文件无后缀名)
-
指定依赖模块的查找路径由 modulesDirectories 改成 modules
-
module 配置
-
外层 loaders 改为 rules
-
内层 loader 改为 use
-
所有插件必须加上 -loader,不再允许缩写
-
不再支持使用!连接插件,改为数组形式
-
json-loader 模块移除,不再需要手动添加,webpack2 会自动处理
-
plugins 配置
-
移除 OccurenceOrderPlugin 跟 NoErrorsPlugin ,改为内置
module.exports = {entry: './src/index.js',output:{ filename:'bundle.js', path:path.resolve(__dirname,'dist')},resolve: { // webpack 1.x extensions: ['', 'js', 'json'], // webpack 2.x extensions: ['js', 'vue', 'json'] // webpack 1.x modulesDirectories: ["node_modules"], // webpack 2.x modules: [path.resolve(__dirname, 'node_modules')]},module: { // webpack 1.x loaders:[ { test:/\.css$/, loader: "style!css!less" } ], // webpack 2.x rules:[ { test:/\.css$/, use:['style-loader','css-loader' ]}]}}
-
v2 -- v3
-
新特性
-
Scope Hoisting
-
使用 ModuleConcatenationPlugin 插件来启用作用域提升
-
代码体积更小,因为函数申明语句会产生大量代码
-
代码在运行时因为创建的函数作用域更少了,内存开销也随之变小
const path = require('path') const { CleanWebpackPlugin} = require('clean-webpack-plugin') const webpack = require('webpack') module.exports = { mode: 'none', entry: './src/index.js', output: { filename: 'bundle.js', path: path.resolve(__dirname, 'dist') }, plugins: [ new CleanWebpackPlugin(), // scope hoisting new webpack.optimize.ModuleConcatenationPlugin() ]}
-
-
-
Magic Comments
-
可以分割代码块,为生成的 chunk 指定 chunkName 并实现懒加载
const path = require('path') const { CleanWebpackPlugin} = require('clean-webpack-plugin') const webpack = require('webpack') module.exports = { mode: 'none', entry: './src/index.js', output: { filename: 'bundle.js', path: path.resolve(__dirname, 'dist') }, plugins: [new CleanWebpackPlugin(), // scope hoisting new webpack.optimize.ModuleConcatenationPlugin() // 分包的chunk id 转为字符串标识符 new webpack.NamedChunksPlugin()]}
-
v3 -- v4
- 新特性
- 自动设置 process.env.NODE_EVN 到不同环境
- development --> new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify("development") })
- production new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify("production") })
- mode (对不同的环境他会启用不同的配置)
- none 去掉所有默认设置
- development
- new webpack.NamedModulesPlugin() 这个插件的作用是在热加载时直接返回更新文件名,而不是文件的 id,给各个模块加上姓名,便于开发的时候查找
- new webpack.NamedChunksPlugin() 把 chunk id 变为一个字符串标识符
- production 默认启用:
- 混淆&压缩 JS
- Scope Hoisting ModuleConcatenationPlugin
- 通过 NoEmitOnErrorsPlugin 在编译出现错误时,直接跳过输出阶段。这样可以确保输出资源不会包含错误
- Code Splitting splitChunks 取代 CommonsChunkPlugin
- flagIncludedChunks 在编译输出 chunks 之间的引用模块的 id,便于直观各代码块之间的关系
- SideEffectsFlagPlugin 去除副作用代码
- 差异
- 分包
- optimization.splitChunks 取代 CommonsChunkPlugin
- 压缩/混淆
- optimization.minimize 为 true ( prod 默认置为 true) 取代 UglifyJsPlugin
v4 -- v5
-
弃用的配置
- v4 中不支持的所有项在 v5 中都被删除了,例如 extract-text-webpack-plugin
-
新特性
-
优化持久缓存
-
Webpack 在执行的时候,以配置的 entry 为入口,递归解析文件依赖,构建一个 graph,记录代码中各个 module 之间的关系。 每当有文件更新的时候, 递归过程会重来,graph 发生改变
-
如果简单粗暴地重建 graph 再编译,会有很大的性能开销。 Webpack 利用缓存实现增量编译,从而提升构建性能
-
在编译的时候会将 graph 以二进制或者 json 文件存储在硬盘上。每当代码变化、模块之间依赖关系改变导致 graph 改变时, Webpack 会读取记录做增量编译
-
使用 cache-loader 可以将编译结果写入硬盘缓存,Webpack 再次构建时如果文件没有发生变化则会直接拉取缓存
-
还有一部分 loader 自带缓存配置,比如 babel-loader,可以配置参数 cacheDirectory 使用缓存,将每次的编译结果写进磁盘(默认在 node_modules/.cache/babel-loader 目录)
-
优化长期缓存(默认值自增 id)
-
针对 moduleId 和 chunkId 的计算方式进行了优化
-
moduleId 根据上下文模块路径,chunkId 根据 chunk 内容计算,最后为 moduleId 和 chunkId 生成 3 - 4 位的数字 id,实现长期缓存,生产环境下默认开启
-
NodeJS 的 polyfill 脚本被移除
-
最开始,Webpack 目标是允许在浏览器中运行 Node 模块。但是现在在 Webpack 看来,大多模块就是专门为前端开发的
-
更好的 TreeShaking
-
v5 中会分析模块 export 与 import 之间的依赖关系,最终的代码生成非常简洁
-
Module Federation
解决不同应用/项目中的模块热拔插(跨应用模块共享)
达到了线上 Runtime 的效果,让代码直接在项目间利用 CDN 直接共享,不再需要本地安装 Npm 包、构建再发布了
-
NPM 方式共享模块(Lib)
需要将依赖作为 Lib 安装到项目,进行 Webpack 打包构建再上线
需要共享一个模块时,最常见的办法就是将其抽成通用依赖并分别安装在各自项目中
-
UMD 方式共享模块(ESI)通用模块定义规范
将模块用 Webpack UMD 模式打包,并输出到其他项目中
缺点:包体积无法达到本地编译时的优化效果,且库之间容易冲突
-
微前端方式共享模块 micro-frontends (MFE)
- 微前端也是最近比较火的模块共享管理方式,微前端就是要解决多项目并存问题,多项目并存的最大问题就是模块共享,不能有冲突
- 资源加载方式上的缺点
- 子应用独立打包,模块更解耦,但无法抽取公共依赖等
- 整体应用一起打包,很好解决上面的问题,但打包速度实在是太慢了,不具备水平扩展能力
-
模块联邦方式(Federated Module)
直接将一个应用的包应用于另一个应用,同时具备整体应用一起打包的公共依赖抽取能力
让应用具备模块化输出能力,其实开辟了一种新的应用形态,即 “中心应用”,这个中心应用用于在线动态分发 Runtime 子模块,并不直接提供给用户使用
const HtmlWebpackPlugin = require("html-webpack-plugin"); const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin"); // app1 module.exports = { plugins: [ new ModuleFederationPlugin({name: "app_one_remote", // // 当前应用名称,需要全局唯一 remotes: { // 可以将其他项目的 name 映射到当前项目中 app_two: "app_two_remote", app_three: "app_three_remote" }, exposes: { // 表示导出的模块,只有在此申明的模块才可以作为远程依赖被使用 AppContainer: "./src/App" }, shared: ["react", "react-dom", "react-router-dom"] // 是非常重要的参数,制定了这个参数,可以让远程加载的模块对应依赖改为使用本地项目的 React 或 ReactDOM }), new HtmlWebpackPlugin({ template: "./public/index.html", chunks: ["main"] }) ] }; // app2 export default { plugins: [ new ModuleFederationPlugin({ name: "app_two", // 当前应用名称,需要全局唯一 library: { type: "var", name: "app_two" }, filename: "remoteEntry.js", exposes: { Search: "./src/Search" }, shared: ["react", "react-dom"] } ) ]};
-