webpack常用配置汇总
webpack常见配置
npm脚本执行流程
运行npm run build
package.json
文件
1 | { |
- 会去package.json文件中的script对象中查找该命令,没有找到报错
- 找到脚本对应的shell命令 执行对应命令
- 查找
\node_modules\.bin\webpack.cmd
如果存在执行 - 如果不存在 则执行全局(
C:\Users\skdzx\AppData\Roaming\npm\node_modules\webpack.cmd
) - 如果找到对应脚本的执行文件,执行对应命令
webpack常用命令
1 | webpack -p //压缩混淆脚本,这个非常非常重要! |
webpack mode
webpack的开发模式 有 development开发模式、production生产模式、node不指定模式
优先级:命令行配置 > 配置文件配置
- 通过在命令行中配置mode
模块中可以通过process.env.NODE_ENV获取,webpack.config中不能获取
1 | // package.json |
- 在命令行中配置env
模块中不能获取,webpack.config中使用函数方式获取
1 | // package.json |
- 使用
webpack.DefinePlugin
配置通用变量
模块中可以获取,webpack.config 中无法获取
1 | // webpack.config.js |
- 使用
cross-env
配置环境变量
1 | // package.json |
webpack entry
可以是字符串,数组,对象方式
entry对应的路径可以是相对路径、可以是绝对路径(output 只能是绝对路径)
1 | //// 单入口 |
webpack output
1 | // 出口 |
loader的三种配置方式
1 | module.exports = { |
webpack source-map
1 | // 开启source-map |
- source-map 配置
关键字 | 含义 |
---|---|
eval | 使用eval包裹模块代码 |
source-map | 最完整的信息,产生.map文件 |
cheap | 不包含列信息(关于列信息的解释下面会有详细介绍)也不包含loader的sourcemap |
module | 包含loader的sourcemap(比如jsx to js ,babel的sourcemap),否则无法定义源文件 |
inline | 将.map作为DataURI嵌入,不单独生成.map文件 |
- source-map 组合标准
1 | ^(inline-|hidden-|eval-)?(nosources-)?(cheap-(module-)?)?source-map$ |
- source-map 常见组合
类型 | 含义 |
---|---|
source-map | 原始代码 最好的sourcemap质量有完整的结果,但是会很慢 |
eval-source-map | 原始代码 同样道理,但是最高的质量和最低的性能 |
cheap-module-eval-source-map | 原始代码(只有行内) 同样道理,但是更高的质量和更低的性能 |
eval-cheap-source-map | 转换代码(行内) 每个模块被eval执行,并且sourcemap作为eval的一个dataurl |
eval | 生成代码 每个模块都被eval执行,并且存在@sourceURL,带eval的构建模式能cache SourceMap |
cheap-source-map | 转换代码(行内) 生成的sourcemap没有列映射,从loaders生成的sourcemap没有被使用 |
cheap-module-source-map | 原始代码(只有行内) 与上面一样除了每行特点的从loader中进行映射 |
source-map
- 生产单独的source-map文件
- 包含完整的行和列信息
在目标文件里建立关系,从而能提示源文件原始位置
- 不能缓存模块的sourceMap,每次都要重新产生完整的sourcemap
inline-source-map
- 以base64格式内联在打包后的文件中
- 包含完整的行和列信息
在目标文件里建立关系,从而能提示源文件原始位置
hidden-source-map
- 会在外部生产source-map,但是在目标文件并没有建立关联,也不能提示原始错误位置
- 线上的代码中存在对应source-map的,有可能会造成代码的泄露
当线上代码出BUG时,可以使用对应的source-map进行调试
eval-source-map
- 会为每一个模块生产一个单独的sourcemap进行关联,并使用eval进行执行
- 它与sourceMap一样好使,但是可以缓存每个模块的sourcemap,在重新构建的时候速度更快。每个模块都有自己的sourcemap,并且模块之间的sourcemap相互独立
nosources-source-map
会生产sourcemap文件,也能找到源代码位置,但是源代码位置是空的
cheap-source-map
- 只包含行映射,不包含列映射
- 映射代码是转换后代码 如使用了babel
cheap-module-source-map
- 只包含行映射,不包含列映射
- 映射到真实源代码文件
eval-cheap-module-source-map
:提供了只映射行(没有列映射)的源映射,而且速度更快。eval-cheap-source-map
:与eval-cheap-source-map类似,但不为模块生成源映射(即jsx到js的映射)。eval
:具有最佳性能,但它只映射到每个模块的已编译源代码。在许多情况下,这已经足够了。(映射到编译后的源代码)
webpack 常用loader
webpack默认执行打包处理js和json,无法处理其他文件内容。loader的作用是将其他文件的内容转化为js可执行的代码
1 | function loader(source){ |
loader的分类
执行顺序 pre > normal(默认)> inline > post (同级别: 先下后上,先右后左)
pre
: 前置normal
: 正常inline
:内联post
: 后置
常见loader
raw-loader
: 用于文本url-loader
url-loader
: 判断是否小于指定大小 小于返回base64 否则返回图片dataUrlfile-loader
:处理图片css-loader
1
css-loader
:处理css
importLoaders
less
阶段会将会处理@import
,["style-loader","css-loader","less-loader"]
css-loader
不需要设置importLoaders
importLoaders
选项允许你配置在css-loader
之前有多少 loader 应用于@import
资源。
less-loader
less-loader
:处理less 具体配置查看 lessOptionsscss-loader
:处理scssvue-loader
:处理vue文件babel-loader
babel-loader
:处理jsts-loader
:处理tspostcss-loader
处理css浏览器兼容 postcssOptionsimage-webpack-loader
:图片压缩thread-loader
thread-loader
: js开启多进程压缩expose-loader
: 实现第三方模块的加载一次全局使用cache-loader
:用于将开销较大的loader存储到磁盘上,第一次构建时间会久
1 | const MiniCssExtractPlugin = require('mini-css-extract-plugin'); |
webpack常用plugins
webpack插件可以理解为,在webpack整体执行流程中有许多的钩子函数,插件通过钩子函数在webpack打包流程中做一些事情
htmlWebpackPlugin
:打包生成html并自动引入scriptMiniCssExtractPlugin
:提取css到单独的文件DefinePlugin
:配置全局变量 webpack内置TerserPlugin
:压缩JS webpack内置了OptimizeCssAssetsWebpackPlugin
: 压缩优化CSSFileManagerPlugin
:用于将打包后的文件复制到指定文件HtmlWebpackExternalsPlugin
: 引入CDN模块ProvidePlugin
: 引入第三方库 webpack内置CopyWebpackPlugin
: copy静态文件CleanWebpackPlugin
:在打包前清空打包文件CompressionWebpackPlugin
: 对代码进压缩BannerPlugin
: 为项目文件插入注释DllPlugin
: 动态链接库 用于将组件库与不变的第三库 进行单独打包DllReferencePlugin
: 用于动态替换SpeedMeasureWebpackPlugin
:打包速度分析插件BundleAnalyzerPlugin
: 开启服务生成打包可视化的html文件
1 | const HtmlWebpackPlugin = require("html-webpack-plugin"); |
webpack devServer
webpack 开发服务器配置
1 | // webpack.config.js |
webpack polyfill
polyfill 用于磨平js在各个浏览器的差异问题
useBuiltIns
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
的用法
- `false` : 此时不对 polyfill 做操作。如果引入 `@babel/polyfill`,则无视配置的浏览器兼容,引入所有的 `polyfill`
- `entry`:**会影响全局变量,因为是在全局对象添加polyfill方法**
- 在项目入口引入一次(多次引入会报错)
- `"useBuiltIns": "entry"` 根据配置的浏览器兼容,引入浏览器不兼容的 polyfill。需要在入口文件手动添加 `import '@babel/polyfill'`,会自动根据 browserslist 替换成浏览器不兼容的所有 polyfill
- 这里需要指定 core-js 的版本, 如果 "corejs": 3, 则 import '@babel/polyfill' 需要改成 `import 'core-js/stable';import 'regenerator-runtime/runtime';`
```JavaScript
// 入口文件
import 'core-js/stable';
import 'regenerator-runtime/runtime';
// webpack.config.js
{
test: /\.jsx?$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: [["@babel/preset-env", {
useBuiltIns: 'entry',
corejs: { version: 3 }
}], "@babel/preset-react"],
plugins: [
["@babel/plugin-proposal-decorators", { legacy: true }],
["@babel/plugin-proposal-class-properties", { loose: true }]
]
}
}
},
// package.json
{
"browserslist": {
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
],
"production": [
">1%"
]
},
}usage
: 按需引入,不需要配置polyfill
,引入方式也是import xxx from 'xxx'
,当多个文件都需要polyfill
时,会重复在局部添加引入代码,不会影响全局变量。babel-runtime
1
2
3
4
5
6
7
8
9
10
11
12
- Babel为了解决全局空间污染的问题,提供了单独的包[babel-runtime](https://babeljs.io/docs/en/babel-runtime)用以提供编译模块的工具函数
- 简单说 `babel-runtime` 更像是一种按需加载的实现,比如你哪里需要使用 Promise,只要在这个文件头部`import Promise from 'babel-runtime/core-js/promise'`就行了
```Plain%20Text
npm i babel-runtime -D
import Promise from 'babel-runtime/core-js/promise';
const p = new Promise(()=> {
});
console.log(p);@babel/plugin-transform-runtime
1
2
3
4
5
6
7
- 启用插件`babel-plugin-transform-runtime`后,Babel就会使用`babel-runtime`下的工具函数。
- `babel-plugin-transform-runtime`插件能够将这些工具函数的代码转换成`require`语句,指向为对`babel-runtime`的引用
- ```
babel-plugin-transform-runtime
就是可以在我们使用新 API 时自动import babel-runtime
里面的polyfill
当我们使用
async/await
时,自动引入babel-runtime/regenerator
当我们使用 ES6 的静态事件或内置对象时,自动引入
1
babel-runtime/core-js
- 移除内联
babel helpers
并替换使用babel-runtime/helpers
来替换
- 移除内联
1 | cnpm i @babel/runtime-corejs2 -D |
main.js
1 | const p = new Promise(()=> {}); |
- 最佳实践
babel-runtime
适合在组件和类库项目中使用,而babel-polyfill
适合在业务项目中使用。
- polyfill-service
- polyfill.io 自动化的 JavaScript Polyfill 服务
- polyfill.io 通过分析请求头信息中的 UserAgent 实现自动加载浏览器所需的 polyfills
1 | <script src="https://polyfill.io/v3/polyfill.min.js"></script> |
webpack resolve使用
1 | module.export = { |
webpack optimization优化
常见的优化配置
1 | const TerserPlugin = require("terser-webpack-plugin") |
minimize
: 开启插件压缩minimizer
: 传入实现压缩功能的插件,minimize
需要设置为truesplitChunk
chunks1
2
3
4
5
代码块切割优化
- chunksmaxAsyncRequests1
2
3
4
5
6
7
8
9
10
11
12
13
代码块切割方式
- `all` : 对引入的(直接引入、按需引入)模块进行优化
- `async` : 入口中按需加载的模块,模块中按需加载的模块,都会被优化
- `initial` : 入口中直接引入的模块、模块中直接引入的模块,都会被拆解并单独打包。共用模块只打包一次
- `minChunks`: 拆分前 非按需引入的共用代码块最小次数
- `maxInitialRequests` : 当项目打包完成后,一个直接引入的包会被拆分为指定个数的包
- maxAsyncRequestsminSize、maxSize1
2
3
4
5
6
7
8
9
10
: 当项目打包完成后,一个按需加载的包会被拆分为指定个数的包
- maxAsyncRequests 用来表示按需加载的模块其能拆分的最大数量;
- 如果对于单独打包出来的模块有两种可能,被多次引入的那个模块或模块集会被优先打包出来;
- 同样的情况,如果两种情况下模块被引用的次数相同,体积大的那个模块或模块集会被打包出来;
- 如果两种情况下,情况一只包含一个模块,情况二则包含两个模块,并且两种情况下被引入的次数也是相同的,前两条规则仍然有效。
- minSize、maxSizename1
2
3
4
5
6
7
8
9
10
11
12
13
: 分别表示 chunk 在被拆分之前的最小体积和最大体积(maxSize 可以小于 minSize)
- minSize 表示被拆分出的 bundle 在拆分之前的体积的最小数值,只有 >= minSize 的 bundle 会被拆分出来;
- maxSize 表示被拆分出的 bundle 在拆分之前的体积的最大数值,默认值为 0,表示 bundle 在拆分前的体积没有上限;
- maxSize 如果为非 0 值时,不可以小于 minSize;
- 如果 bundle 在被拆分前的体积大于 maxSize,webpack 将会尝试将它拆分成更小的模块(前提是 bundle 在拆分之前由多个模块组成,如果仅仅只包含一个模块,大于 maxSize 和大于 minSize 小于等于 maxSize 是一样的效果);
- 应用 maxSize 打包的 bundle 其名称会由“不应用 maxSize 时产生的 bundle 名称”和“一个生成的 key 值”组成;
- maxSize 对于直接引入(import 或 require 引入)的部分或者按需引入的部分都有效,不过有些许区别,按需引入的包如果被拆分则使用 chunk 的名称作为 bundle 名,不会像前面的案例那样包含一些 key,如果非按需引入的部分包含于入口点名称构建的 bundle 时,该 bundle 名称将包含前面案例中类似的 key;
- minSize 对于按需引入的部分是没有效的,因为无论在什么情况下,按需引入的部分都会被拆分打包出来;
- name: 缓存组,可以使用并覆盖splitChunks的配置项。拥有三个特定的属性 - test: 表示要过滤 modules,默认为所有的 modules,可匹配模块路径或 chunk 名字,当匹配到某个 chunk 的名字时,这个 chunk 里面引入的所有 module 都会选中; - priority:权重,数字越大表示优先级越高。一个 module 可能会满足多个 cacheGroups 的正则匹配,到底将哪个缓存组应用于这个 module,取决于优先级;默认为辅助 - reuseExistingChunk:表示是否使用已有的 chunk,true 则表示如果当前的 chunk 包含的模块已经被抽取出去了,那么将不会重新生成新的,即几个 chunk 复用被拆分出去的一个 module;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
: 这个配置用于控制 webpack 打包时被拆分出来的 bundle 的名称。
- splitChunks.name 用于控制 webpack 打包时被拆分出来的 bundle 的名称;
- 该配置的值有三种选择,可以是一个布尔值(true 和 false),也可以是一个函数(形式如 function (module, chunks, cacheGroupKey) => string),又或许是一个单纯的 String 类型;
- 该配置也可以在缓存组中单独配置,如:splitChunks.cacheGroups.{缓存组的名称}.name;
- 如果配置为一个布尔值,比如默认下该配置为 true,对于生成的 chunk 的名称,将会基于打包过程中 chunks 和缓存组名称自动生成,如果值为 false,将会直接使用 chunk 名称;
- 你可以通过给该配置配置一个字符串或者函数来自定义定制打包后 chunk 的名称。如果配置的字符串是静态的或者配置的函数返回的是一个静态的字符串,将会使得被另外单独拆分的所有 chunk 都被打包到一个单独的文件中,这会导致页面首次加载增加,减慢页面的加载;
- 如果给该配置赋值为一个函数,我们可以很好地利用参数中的 chunk.name 和 chunk.hash 来定制打包后生成的 name(这里所说的 chunk 是参数 chunks 参数的某一项,chunks 是所有 chunk 的集合);
- 如果 splitChunks.name 匹配到一个入口点名称,打包后生成的 bundle 中该入口点将会被删除;
- 对于按需引入的模块,仅在 chunks 为 all 时有效,并且值得注意的是,如果 name 的赋值形式是 function,并根据 chunks 等信息来自定义 name 规则时,只对直接引入的部分有效,对按需引入无效,而对于返回一个静态字符串和直接赋值静态字符串是有效的,其他的情况也是有效的;
- 官方建议在生产环境时将 name 设置为 false,为了“it doesn't change names unnecessarily”(这将保证不会不必要地更改名称),具体怎样去理解个人暂时还不清楚;
- ```
cacheGroups
tree-shaking 原理
利用es6模块的特点:
- 只能作为模块顶层的语句出现
- import的模块名只能是字符串常量,不能动态引入
- import binding是immutable,引入的模块不能再做修改
webpack hash
webpack中hash分为 hash、chunkhash、contenthash
hash:也被叫做文件指纹,是指打包后输出的文件名和后缀
hash一般是结合CDN缓存来使用,通过webpack构建之后,生产对应文件名自动带上对应的MD5值。如果文件内容发生改变时contenthash也会发生变化,对应的HTML引用的URL地址也会发生变化,触发CDN服务器从源代码服务器上拉取最新的文件资源更新本地缓存
常见的webpack占位符
占位符名称 | 含义 |
---|---|
ext | 资源后缀名 |
name | 文件名称 |
path | 文件的相对路径 |
folder | 文件所在的文件夹 |
hash | 每次webpack构建时生成一个唯一的hash值 |
chunkhash | 根据chunk生成hash值,来源于同一个chunk,则hash值就一样 |
contenthash | 根据内容生成hash值,文件内容相同hash值就相同 |
hash
- Hash 是整个项目的hash值,其根据每次编译内容计算得到,每次编译之后都会生成新的hash,即修改任何文件都会导致所有文件的hash发生改变
chunkhash
采用hash计算的话,每一次构建后生成的哈希值都不一样,即使文件内容压根没有改变。这样子是没办法实现缓存效果,我们需要换另一种哈希值计算方式,即chunkhash
- chunkhash和hash不一样,它根据不同的入口文件(Entry)进行依赖文件解析、构建对应的chunk,生成对应的哈希值。我们在生产环境里把一些公共库和程序入口文件区分开,单独打包构建,接着我们采用chunkhash的方式生成哈希值,那么只要我们不改动公共库的代码,就可以保证其哈希值不会受影响
contenthash
- 使用chunkhash存在一个问题,就是当在一个JS文件中引入CSS文件,编译后它们的hash是相同的,而且只要js文件发生改变 ,关联的css文件hash也会改变,这个时候可以使用
mini-css-extract-plugin
里的contenthash
值,保证即使css文件所处的模块里就算其他文件内容改变,只要css文件内容不变,那么不会重复构建
webpack debugger测试
1 | const webpack = require("webpack"); |
dotenv 配置多个环境变量
- 生成
.env
文件
1 | // .env |
- 将文件内容转为变量
1 | const dotenvFile = ".env"; |
loader-runner 文件
loader的执行流程
loader的核心执行流程 loader-runner.js文件
注意:
- 如果
loader
需要返回多个值的时候,必须要this.callback(err,...args)
这种方式返回,不要使用return
- 上一个
loader
的返回值 会作为下一个loader
的参数
1 | var fs = require("fs"); |
简述: loader-runner 主要将loader转为对象loader自身函数转为normal属性,pitch函数转为pitch属性,通过runSyncOrAsync执行对应的loader的normal或pitch,iterateNormalLoaders与iteratePitchLoaders作用相似都是递归执行对应函数,其中iteratePitchLoaders会判断pitch函数是否有返回值,如果有返回值 则不会在执行当前loader之前的loader与当前pitch之后的pitch
- 标题: webpack常用配置汇总
- 作者: 爱吃猪头爱吃肉
- 创建于: 2023-04-25 08:03:09
- 更新于: 2023-04-25 08:03:16
- 链接: https://zsasjy.github.io/2023/04/25/webpack常用配置汇总/
- 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。