webpack5
重新整理一下 webpack 知识,内容过多,了解功能即可,遇到 api 查文档。
mode
webpack 4.x 引入了 mode 的概念。webpack
mode 默认为 production, webpack serve
默认为 development。命令行中的 mode 比 webpack.config.js 的 mode 优先级更高。
生产模式默认会启用各种性能优化的功能,开启代码压缩,运行时不打印 debug 信息,静态文件不包括 sourcemap,包括构建结果优化以及 webpack 运行性能优化。
会将 process.env.NODE_ENV 设为 development,启用 NamedChunksPlugin 和 NamedModulesPlugin
环境功能:需要生成 sourcemap,需要打印 debug,需要 live reload 或 hot reload 功能
开发模式会开启 debug 工具,需要热加载,不进行代码压缩,包含 sourcemap 文件,运行时打印详细的错误信息,以及更加快速的增量编译构建
会将 process.env.NODE_ENV 设为 production,启用 FlagDependencyUsagePlugin, FlagIncludedChunksPlugin, ModuleConcatenationPlugin, NoEmitOnErrorsPlugin, OccurrenceOrderPlugin, SideEffectsFlagPlugin, UglifyJsPlugin
环境功能:可能需要分离 CSS 成单独的文件,以便多个页面共享同一个 CSS 文件,需要压缩 HTML/CSS/JS 代码,需要压缩图片
区分环境
--mode=production
用来设置模块内的 process.env.NODE_ENV,即可以在 ./src/index.js 通过 process.env.NODE_ENV 得到 production 这个值,但不能在 webpack.config.js 中得到这个值(得到 undefined)--env=production
用来设置 webpack 配置文件的函数参数,webpack.config.js 的导出可以是一个函数,会传入 env 这个参数,此时可以通过 env 得到 production 这个值。但 process.env.NODE_ENV 仍为 undefinedcross-env
用来设置 node 环境的 process.env.NODE_ENV,可以在 webpack.config.js 中得到 production 的值了。cross-env NODE_ENV=production
实际上是对 window,linux,mac 里设置了一个环境变量为 product,需要 npm i -D 到项目中,是真正的环境变量,在 window 中相当于set NODE_ENV=production
,mac、linux 中相当于export NODE_ENV=production
。只能在 webpack.config.js 都能拿到 process.env.NODE_ENVwebpack.DefinePlugin
是一个 plugin, 设置后可以在 webpack.config.js 和 ./src/index.js 都拿到 process.env.NODE_ENV,是一个字符串替换的过程。用法:在 plugins 数组中
new webpack.DefinePlugin({ “process.env.NODE_ENV”: JSON.stringify(process.env.NODE_ENV) /* 相当于变成 '"product"' 了 */ })
一般 cross-env 和 webpack.DefinePlugin 一起使用是比较完美的方案。
externals
配置外部模块,key 模块名,value 全局变量名。
1 | // webpack.config.js |
watch
1 | module.exports = { |
loader
webpack 只识别 js 、 json 和 wasm,其他后缀文件需要用 loader 解析。所有的 loader 都需要通过 npm 安装后才能使用。
loader 有四种分类,pre、post、normal、inline,通过 enforce 属性指定这个值,默认为 normal,表示 loader 的执行顺序,pre 是先执行,post 是后执行,normal 在中间。 pre -> normal -> post
1 | let rules = [ |
- css-loader 用来翻译处理 @import 和 url()
- style-loader 可以把 css 转成 js,之后作为 <style> 标签插入到 dom 中
- postcss-loader 处理 css 兼容性的 loader,可以加上各种厂商前缀
- sass-loader、less-loader、stylus-loader 可以把对应格式的文件转为 css,需要 npm i 对应的 css 预处理器
- file-loader 可以把 src 目录里依赖的源图片文件拷贝到目标目录里去,文件名一般为新的 hash 值
loader 与 plugin:loader 用于转换某些类型的模块,而插件则可以用于执行范围更广的任务。包括:打包优化,资源管理,注入环境变量
全局暴露 loader
expose-loader 可以把一个变量放在全局对象。注意,如果想要在生成的 html 文件中的 script 标签里使用这个变量,需要加上 defer,因为包裹 webpack 打包出来的 js 文件的 script 标签有 defer 这个属性,会导致还未执行(未注入变量)打包后 js 时已经走了你自己的 script,从而找不到这个变量。
1 | // webpack.config.js |
babel
- babel-loader 使用 babel 和 webpack 转译 js 文件,只是一个转换函数,不知道如何转换
- @babel/core 这是 babel 编译的核心包,可以识别高级的 js 代码,但不知道该转换成什么
- @babel/preset-env 是 babel 的兼容性预设,包含 es6 -> es5 的所有 plugins,不包含以下 plugin,决定将高级 js 语法转换成什么语法
- @babel/preset-react 是包含 React 相关 plugin 的 Babel 预设
- @babel/plugin-proposal-decorators 把类和对象装饰器转成 ES5,如
@connect class O { @readonly PI = 3.14 }
- @babel/plugin-proposal-class-properties 转换静态类属性以及使用属性初始化语法声明的类型
class P { static a = 1; b = 2 }
babel 的 plugin 决定将高级 js 语法转换成什么语法,每个插件对应一个语法,preset-env 预设就是多个插件的集合。比如
plugin-transform-arrow-functions
就是箭头函数转成普通函数
eslint
eslint-loader 可以进行代码的检测,这个 loader 为 enforce: 'pre'
,webpack 中设置 loader 的 options: { fix: true }
可以开启遇到问题自动修复。
rules 中 的类型
- off 表示不检查这项
- error 表示发现就报错
在目录下新建 .eslintrc.js
1 | module.exports = { |
由于 eslint 的 fix 只发生在 npm run build 的时候,比较不及时,如果希望每次保存都触发 eslint,可以在项目根目录下新建 .vscode 文件夹(记得放入 .gitignore),再新建 settings.json 配置文件,写入配置,再安装 eslint 插件即可。
这个 .vscode 文件夹及里面的 settings.json 也可以通过在 VSCode 中通过 Edit/Preferences/Settings 中修改 Workspace Tab 的配置文件后自动生成。
1 | // /.vscode/settings.json |
npm i eslint eslint-loader babel-eslint -D
LF: LF 就是 linefolder(换行)
CRLF: CR 就是 carriage return(回车)
window 里换行符是 \r\n, \r 是回车,\n 是换行,这样写的来源是老式打印机,里面有个车存放墨盒,回车表示打印墨盒回到本行最前面,换行表示去下一行的同一个位置,现在其实已经不需要了,但习惯被保留下来。
unix 里换行符是 \n,没有 \r
mac 里换行符是 \r
VSCODE 右下角可以修改你的回车 CRLF 还是 LF。
Sourcemap
在 webpack 的 devtool 可以配置以下类型,控制 sourcemap 的生成,设置 false 不生成。
生成的源码里会通过在最后一行加上 //# sourceMappingURL=main.js.map
来关联 map
类型 | 含义 |
---|---|
source-map | 生成完整的 sourcemap,并且建立关联,有行列信息 |
eval-source-map | 为每一个模块生成一个单独的 sourcemap 文件进行内联,并使用 eval 执行,由于每个模块的 sourcemap 分开了,所以有对 sourcemap 的缓存功能 |
cheap-module-eval-source-map | 原始代码(只有行内),没有列信息,但有 loader 信息 |
cheap-eval-source-map | 用 eval 包裹代码,只有行信息,没有 loader 信息 |
eval | 用 eval 包裹代码,既有行也有列,还有 loader |
cheap-source-map | 外部生成 sourcemap 文件,不包含列和 loader 的 map |
cheap-module-source-map | 外部生成 sourcemap 文件,不包含列的 map(但包含 loader) |
关键字
实际上这些类型都是六个关键字的组合:
- eval 使用 eval 包裹代码
- source-map 产生 .map 文件,包含原始代码信息(即包含 loader 的 sourcemap),包含行列信息
- cheap 不包含列信息,也不包含 loader 的 sourcemap
- module 包含 loader 的 sourcemap
- inline sourcemap 以 base64 内嵌到打包的源码中,不单独生成 .map 文件
- hidden 在目标源码中不建立关联 map
关键字可以任意组合,但是有顺序要求
[inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map
开发环境
如果想要速度快, eval-cheap-source-map,想要调试更友好,cheap-module-source-map。
一般推荐用 eval-source-map。
生产环境
先排除内联,因为我们要隐藏源代码和减少文件体积。
想调试友好选择 source-map>cheap-source-map/cheap-module-source-map>hidden-source-map/nosources-source-map
想速度快, cheap
这种就是 hidden-source-map(生成 map,但不在源码中关联 .map),devtool 的值为 hidden-source-map,引入 filemanager-webpack-plugin,拷贝 dist 下的 .map 文件至另一个文件夹后,删除 dist 下的 .map 文件。
需要调试时,添加指定路径的 .map 即可。
测试环境
把 devtool 设置为 false,引入 filemanager-webpack-plugin,我们需要更精细地控制 devtool
假设测试环境是在 dist 目录下开启了 http-server,那么它的 .map 会指向本机的 8081 (假设在 /maps 开启了一个 http-server -p 8081)目录下的 map。
需要把谷歌的 Enable Javascript source maps 打开。
如果这时候有 debugger,可以看到 webpack:// ,点过去就是源码了。
1 | const webpack = require('webpack') |
Plugin
1 | const webpack = require('webpack') |
devServer
1 | module.exports = { |
也可以在 express 中加入 webpackDevMiddleware 实现跨域,适用于已经有一个 express 项目在跑了,不想开 webpack,就可以集成,达到跨域效果
1 | let express = require('express') |
示例配置
webpack.config.js
webpack4 及 webpack5 的前期版本适用。
css-loader 最高到 5.2,如果再高,会和 file-loader 及 url-loader 冲突。
webpack4
1 | // webpack.config.js |
webpack5
webpack5 中移除了 file-loader、url-loader、raw-loader(处理 Buffer 的,比如打包了个 pdf),并且官方不再使用。新加入的是 type 字段,见这里。
- file-loader -> asset/resource
- url-loader -> asset/inline
- raw-loader -> asset/source
- url-loader + limit(转 base64 的) -> asset
如果要使用原来的 loader 的话,需要加上 type: ‘javascript/auto’,例子
1 | module.exports = { |
postcss.config.js
1 | // postcss.config.js |
package.json
1 | { |
loader-runner
pitch
先 pitch 再 normal
tapable