Hitsuki9's Blog.

webpack4 配置详解

字数统计: 4.3k阅读时长: 17 min
2019/09/23 Share

打包流程

webpack打包流程

 

entry

string | [string] | object { \<key\>: string | [string] } | (function: () => string | [string] | object { \<key\>: string | [string] })

如果传入一个字符串或字符串数组,chunk 会被命名为 main。如果传入一个对象,则每个键(key)会是 chunk 的名称。

 

output

output.filename

string function

此选项决定了每个输出 bundle 的名称。这些 bundle 将写入到 output.path 选项指定的目录下。

对于单个入口起点,filename 会是一个静态名称。

当通过多个入口起点、代码拆分或各种插件创建多个 bundle,应该使用以下替换方式来赋予每个 bundle 一个唯一的名称。

  1. 使用入口名称
1
filename: '[name].bundle.js';
  1. 使用内部 chunk id
1
filename: '[id].bundle.js';
  1. 使用每次构建过程中,唯一的 hash 生成
1
filename: '[name].[hash].bundle.js';
  1. 使用基于每个 chunk 内容的 hash
1
filename: '[chunkhash].bundle.js';

[hash][chunkhash] 的长度可以使用 [hash:16](默认为 20)来指定,或者通过指定output.hashDigestLength 在全局配置长度。

在使用 ExtractTextWebpackPlugin 时,可以用 [contenthash] 来获取提取文件的 hash。

output.chunkFilename 的区别:chunkFilename 是未被列在 entry 中,却又需要被打包出来的文件的命名配置。在按需加载(异步)模块的时候,模块的文件是没有被列在 entry 中的。

1
component: () => import('...');

 

output.path

string

对应一个输出打包结果的绝对路径

 

output.publicPath

string function

只在 production 模式下有效,设置静态资源相对路径的前缀地址,不会影响打包输出的路径(output.path)。

 

output.libraryTarget

string

配置如何暴露 library,此选项与分配给 output.library 的值一同使用。

3.3.1 ~ 3.3.2 为暴露为一个变量。

3.3.3 ~ 3.3.6 为暴露给对象的一个属性。

3.3.7 ~ 3.3.9 为暴露为一个模块。

libraryTarget: “var”

默认值,当 library 加载完成,入口起点的返回值将分配给一个变量。

1
2
3
var MyLibrary = _entry_return_;

MyLibrary.doSomething();

当使用此选项时,将 output.library 设置为空,会因为没有变量导致无法赋值。


libraryTarget: “assign”

这将产生一个隐含的全局变量,可能会潜在地重新分配到全局中已存在的值(谨慎使用)。

1
MyLibrary = _entry_return_; // 如果 MyLibrary 在作用域中未在前面代码进行定义,则你的 library 将被设置在全局作用域内

libraryTarget: “this”

入口起点的返回值将分配给 this 的一个属性(由 output.library 定义)下。

1
2
3
this['MyLibrary'] = _entry_return_;

this.MyLibrary.doSomething();

libraryTarget: “window”

入口起点的返回值将使用 output.library 中定义的值,分配给 window 对象的这个属性下。


libraryTarget: “global”

libraryTarget: "window"


libraryTarget: “commonjs”

入口起点的返回值将使用 output.library 中定义的值,分配给 exports 对象。

1
2
3
exports['MyLibrary'] = _entry_return_;

require('MyLibrary').MyLibrary.doSomething();

不设置 output.library 将导致由入口起点返回的所有属性,都会被赋值给给定的对象,且并不会检查现有的属性名是否存在。(3.3.3 ~ 3.3.6)


libraryTarget: “commonjs2”

入口起点的返回值将分配给 module.exports 对象。

1
2
3
module.exports = _entry_return_;

require('MyLibrary').doSomething();

output.library 会被省略,因此对于此特定的 output.libraryTarget,无需再设置 output.library


libraryTarget: “amd”

将 library 暴露为 AMD 模块。


libraryTarget: “umd”

将 library 暴露为所有的模块定义下都可运行的方式。它将在 CommonJS, AMD 环境下运行,或将模块导出到 global 下的变量。

省略 output.library 会导致将入口起点返回的所有属性,直接赋值给 root 对象。(同 3.3.6 引用)

可以将 output.library 指定为一个对象,用于给每个 target 起不同的名称。

1
2
3
4
5
6
7
8
output: {
library: {
root: "MyLibrary",
amd: "my-library",
commonjs: "my-common-library"
},
libraryTarget: "umd"
}

libraryTarget: “jsonp”

将把入口起点的返回值,包裹到一个 jsonp 包装容器中。

1
MyLibrary(_entry_return_);

 

output.libraryExport

stringstring[]

配置通过 output.libraryTarget 暴露的模块,可以使用于任何的 output.libraryTarget 值。以下用 libraryTarget: "var" 为例。

libraryExport: “default”

将入口起点返回值的默认导出分配给 target。

1
var MyDefaultModule = _entry_return_.default;

libraryExport: “MyModule”

将指定的 library 模块分配给 target。

1
var MyModule = _entry_return_.MyModule;

libraryExport: [“MyModule”, “MySubModule”]

数组被解析为要分配给 target 的 library 模块的路径。

1
var MySubModule = _entry_return_.MyModule.MySubModule;

 

resolve

resolve.alias

object

创建 importrequire 的别名,来确保模块引入变得更简单。

可以在给定对象的键后的末尾添加 $,以表示精准匹配。

1
2
3
4
5
6
alias: {
xyz$: path.resolve(__dirname, 'path/to/file.js');
}

import Test1 from 'xyz'; // 精确匹配,所以 path/to/file.js 被解析和导入
import Test2 from 'xyz/file.js'; // 非精确匹配,触发普通解析

 

resolve.extensions

array

自动解析确定的扩展。默认值为:

1
extensions: ['.js', '.json'];

使用此选项,会覆盖默认数组,这就意味着 webpack 将不再尝试使用默认扩展来解析模块。

 

resolve.mainFiles

array

解析目录(文件夹)时要使用的文件名。默认:

1
mainFiles: ['index'];

 

resolve.modules

array

告诉 webpack 解析模块时应该搜索的目录,绝对路径和相对路径都能使用。

路径在数组中越靠前,则越优先搜索。

使用相对路径将类似于 Node 查找 ‘node_modules’ 的方式进行查找(通过查看当前目录以及祖先路径)。
使用绝对路径,将只在给定目录中搜索。

 

module

module.rules

array

 

匹配条件

  • { test: Condition }:匹配特定条件。一般是提供一个正则表达式或正则表达式的数组。

  • { include: Condition }:匹配特定条件。一般是提供一个字符串或者字符串数组。

  • { exclude: Condition }:排除特定条件。一般是提供一个字符串或字符串数组。

 

Rule.loader

Rule.loaderRule.use: [ { loader } ] 的简写。

 

Rule.use

传递字符串(如:use: [ "style-loader" ])是 loader 属性的简写方式(如:use: [ { loader: "style-loader "} ])。

可以有一个 options 属性为字符串或对象,值可以传递到 loader 中,可以理解为 loader 的参数。

1
2
3
4
5
6
7
8
9
use: [
'style-loader',
{
loader: 'css-loader',
options: {
modules: true
}
}
];

一组 loader 的执行顺序默认是从右到左,一个 loader 接收到的内容是上一个 loader 的处理结果,该过程与”事件冒泡”类似,若希望在”捕获”阶段就执行 loader 的一些方法,可使用 loader.pitch 接口。

 

Rule.enforce

"pre" | "post"

指定 loader 种类,没有值表示的是普通 loader。

所有 loader 通过 前置, 行内, 普通, 后置 排序,并按此顺序使用。

 

Rule.resourceQuery

resourceQuery 可以根据引入文件路径的参数来匹配文件。

vue-loader 中就是通过拼接不同的 query 参数,根据 resourceQuery 并将各个标签分配给对应的 loader 进行处理。

 

loader

loader.pitch

在实际(从右到左)执行 loader 之前,会先从左到右调用 loader 上的 pitch 方法。

对于以下配置

1
use: ['a-loader', 'b-loader', 'c-loader'];

执行流程如下

1
2
3
4
5
6
7
|- a-loader `pitch`
|- b-loader `pitch`
|- c-loader `pitch`
|- requested module is picked up as a dependency
|- c-loader normal execution
|- b-loader normal execution
|- a-loader normal execution

如果某个 loader 在 pitch 方法中给出一个结果,那么将会跳过剩下的 loader


css-loader

加载并解析导入的 .css 文件。

options.importLoaders

用于配置 css-loader 作用于 @import 的资源之前有多少个 loader。


style-loader

css-loader 解析得到的内容注入到 HTML 页面中的 \<style\> 标签中。


babel-loader

babel 总共分为三个阶段:解析,转换,生成。

babel转化过程

babel 本身是不具备转化功能的,提供这些转化功能的是一个个的 plugin。

配置文件

  1. babel.config.js(全局配置)
1
2
3
4
5
6
7
8
9
10
11
module.exports = function (api) {
api.cache(true);

const presets = [ ... ];
const plugins = [ ... ];

return {
presets,
plugins
};
}
  1. .babelrc(局部配置)
1
2
3
4
{
"presets": [...],
"plugins": [...]
}

@babel/core

核心包,用于 parse 和 generate ,可以在代码里直接使用。

1
2
const babel = require('@babel/core');
babel.transform('code', optionsObject);

optionsObject 和 babel.config.js 配置相同。

plugins

插件,告诉 babel 如何转换你的源码。

@babel/plugin-transform-runtime

为了避免多次编译出 helper 函数,需要依赖 @babel/runtime@babel/runtime 包声明了所有需要用到的帮助函数。

@babel/plugin-transform-runtime 会为代码创建一个沙盒环境,解决了 polyfill 提供的类污染全局作用域的情况。

"foobar".includes("foo") 这样的实例方法仍然不能正常执行,因为他在挂载在 String.prototype 上的,如果需要使用这样的实例方法,还是得使用 polyfill 。

@babel/runtime 内部集成了 core-jsregeneratorhelpers 等。

presets

预设,包含了一组 plugin。

@babel/preset-env

只转换语法,例如箭头函数,for...of\*\* 等等,若想实现 ArrayObject 等类上的新方法,以及实现了 PromiseSymbol 这样的新类,需要引入 pollyfill(@babel/pollyfill 已于 babel 7.4 被废弃,若需要使用 pollyfill, 引入 core-js 即可,事实上 @babel/pollyfill 即为 regenerator-runtimecore-js 的封装

当使用 @babel/preset-env 转换 async/awaitgenerator 语法时,可能会报 ReferenceError: regeneratorRuntime is not defined 错误,因为这需要 plugin-transform-regenerator 使用 regenerator 来转换,但是它本身不包括 regeneratorRuntime ,所以需要引入 regenerator-runtime 来使 regeneratorRuntime 存在。通常情况下,使用 @babel/plugin-transform-runtime 即可。

useBuiltIns
  • usage:按需引入 pollyfill 。

  • false: 不使用 pollyfill 。

corejs

设置使用的 core-js(pollyfill)的版本,默认为 2

@babel/preset-react

转换 jsx。

常用配置

1
2
3
4
5
6
7
8
9
10
// 需要安装依赖 core-js 和 @babel/runtime
{
"presets": [[
"@babel/preset-env", {
useBuiltIns: "usage",
corejs: 3
}
]],
"plugins": ["@babel/plugin-transform-runtime"]
}

postcss-loader

配置文件

.postcssrc.jspostcss.config.js

1
2
3
4
5
6
7
8
9
module.exports = {
parser: 'sugarss', // css的缩进语法解析器
plugins: {
'postcss-import': {},
'postcss-cssnext': {},
autoprefixer: {},
cssnano: {}
}
};

autoprefixer

自动添加浏览器前缀。

postcss-import

通过内联内容来转换 @import 规则。

postcss-cssnext

允许使用未来的 css 特性,并做一些兼容处理。

cssnano

压缩 css,包括压缩颜色,删除注释,丢弃重写的规则,合并内容相同的规则等等。


react-hot-loader

记录 react 页面留存状态 state。

文档


eslint-loader

options.formatter

指定错误报告的格式规范,默认为 stylish,可用第三方插件 eslint-friendly-formatter

配置文件

.eslintrc.* 或 .eslintrc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{
"extends": "eslint:recommended", // 启用推荐的规则
"parserOptions": { // 指定想要支持的 JavaScript 语言选项
"ecmaVersion": 6, // 支持 es6
"sourceType": "module" // 支持 es module
},
"env": { // 定义了指定环境中的一组预定义的全局变量
"browser": true
},
/**
* "off" 或 0 - 关闭规则
* "warn" 或 1 - 开启规则,使用警告级别的错误:warn (不会导致程序退出)
* "error" 或 2 - 开启规则,使用错误级别的错误:error (当被触发的时候,程序会退出)
**/
"rules": {
// ...
}
"globals": { // 定义额外的全局变量
"require": false
}
}

svg-sprite-loader

生成 svg 雪碧图。

工作原理: 利用 svgsymbol 元素,将每个 icon 包裹在 symbol 中,并通过 use 元素使用该 symbol

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<svg>
<symbol id="icon名1">
<!-- 省略的 icon path -->
</symbol>
<symbol id="icon名2">
<!-- 省略的 icon path -->
</symbol>
</svg>

<svg>
<use xlink:href="icon名1"></use>
</svg>
<svg>
<use xlink:href="icon名2"></use>
</svg>

svg-sprite-loader 会把你指定的 svg 中的内容都放到一个个的 symbol 中,symbolid 如果不特别指定,那就是文件名。最终将会在 html 中嵌入一个带 symbolsvg


file-loader

默认情况下,生成的文件的文件名就是文件内容的 MD5 哈希值,并会保留所引用资源的原始扩展名。

1
2
3
import img from './file.png';
// ↓
('/0dcbbaa7013869e351f.png');

url-loader

类似于 file-loader,但是在文件大小(单位 byte)低于指定的限制(options.limit)时,可以返回一个 DataURL。

 

plugins

HtmlWebpackPlugin

该插件将生成一个 HTML5 文件, 其中 body 中将包含使用 script 标签引入的打包输出文件(于 output.filename 的值对应)。

1
2
3
4
5
6
7
8
9
10
11
12
const HtmlWebpackPlugin = require('html-webpack-plugin');

plugins: [
new HtmlWebpackPlugin({
title: 'Hello World',
minify: {
// ...
},
filename: 'index.html',
template: 'index.html'
})
];

title

生成的 HTML 文件的标题。


filename

输出的 HTML 文件名。


template

HTML 模板所在的文件路径。根据指定的模板文件来生成特定的 HTML 文件,模板可以是 htmljadeejs 等等,但是需要对应的 loader。如果设置的 titlefilename 与模板发生了冲突,那么以设置的 titlefilename 为准。


inject

注入选项。

  1. true :默认值,script 标签位于 HTML 文件中的 body 内,且为最底部。

  2. body :同 true

  3. headscript 标签位于 head 标签内。

  4. false :不插入生成的 js 文件,只是单纯的生成一个 HTML 文件。


minify

对 HTML 文件进行压缩,可选值是一个压缩选项或者 false,默认值为 false, 即不对生成的 HTML 文件进行压缩。

常用配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
minify: {
caseSensitive: true, // 是否对大小写敏感,默认 false
collapseBooleanAttributes: true, // 是否简写 boolean 格式的属性,如:disabled="disabled" 简写为 disabled,默认 false
collapseWhitespace: true, // 是否去除空格,默认 false
minifyCSS: true, // 是否压缩 HTML 里的 css(使用 clean-css 进行压缩),默认 false
minifyJS: true, // 是否压缩 HTML 里的 js(使用 uglify-js 进行压缩),默认 false
preventAttributesEscaping: true, // 防止属性值的转义,默认 false
removeAttributeQuotes: true, // 是否移除属性的引号,默认 false
removeComments: true, // 是否移除注释,但是会保留 script 和 style 中的注释,默认 false
removeCommentsFromCDATA: true, // 是否删除脚本和样式中的注释,默认 false
removeEmptyAttributes: true, // 是否删除空属性,默认 false
removeOptionalTags: false, // 若开启此项,生成的 HTML 中没有 body 和 head,html 也未闭合,默认 false
removeRedundantAttributes: true, // 是否删除重复多余的属性,默认 false
removeScriptTypeAttributes: true, // 是否删除 script 的 type 属性(type="text/javascript"),默认 false
removeStyleLinkTypeAttributes: true, // 是否删除 style 的 type 属性(type="text/css"),默认 false
useShortDoctype: true, // 是否使用短的文档类型,默认 false
}

hash

此选项的作用是给生成的 js 文件添加一个 hash 值(以类似 query 的形式添加在文件名后,而并非添加在文件名中),该 hash 值是此次 webpack 编译得出的 hash 值,默认值为 false

1
2
3
4
5
plugins: [
new HtmlWebpackPlugin({
hash: true
})
];

编译打包后

1
<script type=text/javascript src=bundle.js?22b9692e22e7be37b57e></script>

cache

默认为 true,表示仅在文件发生更改时才生成一个新的文件。


showError

默认为 true,表示若 webpack 编译过程发生错误,错误的详细信息将会写入生成的 HTML 文件中。


chunks

选择需要引入 HTML 文件的打包后的 js 文件(多出口),如果没有指定 chunks 选项,默认会全部引用。

1
2
3
4
5
6
7
8
9
10
entry: {
index: path.resolve(__dirname, './src/index.js'),
vendor: path.resolve(__dirname, './src/vendor.js')
}

plugins: [
new httpWebpackPlugin({
chunks: ['index']
})
]

excludeChunks

排除掉一些将要引入 HTML 文件的打包后的 js 文件(多出口)。

1
2
3
4
5
6
7
8
9
10
entry: {
index: path.resolve(__dirname, './src/index.js'),
vendor: path.resolve(__dirname, './src/vendor.js')
}

plugins: [
new httpWebpackPlugin({
excludeChunks: ['vendor']
})
]

 

webpack.HotModuleReplacementPlugin

永远不要在生产环境(production)下启用 HMR。

模块热替换(HMR - Hot Module Replacement)功能会在应用程序运行过程中替换、添加或删除模块,而无需重新加载整个页面。

使用 webpack.HotModuleReplacementPlugin 并将 devServerhot 设置为 true 可启用 HMR。

 

MiniCssExtractPlugin

将 css 提取至单独的 .css 文件中。

css 文件可以和 js 文件并行下载,也可以单独缓存,从而可以提高页面加载效率。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{ // loader
test: /\.scss$/,
use: [
{
loader: MiniCssExtractPlugin.loader, // 替换 style-loader
options: {
publicPath: '/',
hmr: process.env.NODE_ENV === 'development'
}
},
'css-loader',
'postcss-loader',
'sass-loader'
]
}

plugins: [
new MiniCssExtractPlugin({
filename: '[name].css', // 同 output.filename
chunkFilename: '[id].css' // 同 output.chunkFilename
})
]

 

webpack.ProvidePlugin

自动加载模块,而不必到处 importrequire

1
2
3
new webpack.ProvidePlugin({
$: 'jquery'
});

任何时候,当 $ 被当作未赋值的变量时,模块就会自动被加载,并且 $ 会被这个模块输出的内容所赋值,然后就可以在任意源码中使用 $ 变量了。

 

webpack.DefinePlugin

DefinePlugin 允许创建一个在编译时可以配置的全局常量,这可能会对开发模式和发布模式的构建允许不同的行为非常有用。

每个传进 DefinePlugin 的键值都是一个标志符或者多个用 . 连接起来的标志符。

  • 如果这个值是一个字符串,它会被当作一个代码片段来使用。

  • 如果这个值不是字符串,它会被转化为字符串(包括函数)。

  • 如果这个值是一个对象,它所有的 key 会被以同样的方式定义。

  • 如果在一个 key 前面加了 typeof,它会被定义为 typeof 调用。

1
2
3
4
5
6
7
new webpack.DefinePlugin({
PRODUCTION: JSON.stringify(true),
VERSION: JSON.stringify('5fa3b9'),
BROWSER_SUPPORTS_HTML5: true,
TWO: '1+1',
'typeof window': JSON.stringify('object')
});

注意,因为这个插件直接执行文本替换,所以给定的值必须包含字符串本身的实际引号。通常,有两种方式来达到这个效果,使用 '"production"',或者使用 JSON.stringify('production')

 

devtool

string false

此选项控制是否生成,以及如何生成 source map。不同的值会明显影响到构建和重新构建的速度。

source map 是为了解决开发代码与实际运行代码不一致时帮助我们 debug 到原始开发代码的技术。

配置项为关键字 evalsource-mapcheapmoduleinline 的任意组合。

eval

CATALOG
  1. 1. 打包流程
  2. 2. entry
  3. 3. output
    1. 3.1. output.filename
    2. 3.2. output.path
    3. 3.3. output.publicPath
    4. 3.4. output.libraryTarget
      1. 3.4.1. libraryTarget: “var”
      2. 3.4.2. libraryTarget: “assign”
      3. 3.4.3. libraryTarget: “this”
      4. 3.4.4. libraryTarget: “window”
      5. 3.4.5. libraryTarget: “global”
      6. 3.4.6. libraryTarget: “commonjs”
      7. 3.4.7. libraryTarget: “commonjs2”
      8. 3.4.8. libraryTarget: “amd”
      9. 3.4.9. libraryTarget: “umd”
      10. 3.4.10. libraryTarget: “jsonp”
    5. 3.5. output.libraryExport
      1. 3.5.1. libraryExport: “default”
      2. 3.5.2. libraryExport: “MyModule”
      3. 3.5.3. libraryExport: [“MyModule”, “MySubModule”]
  4. 4. resolve
    1. 4.1. resolve.alias
    2. 4.2. resolve.extensions
    3. 4.3. resolve.mainFiles
    4. 4.4. resolve.modules
  5. 5. module
    1. 5.1. module.rules
    2. 5.2. 匹配条件
    3. 5.3. Rule.loader
    4. 5.4. Rule.use
    5. 5.5. Rule.enforce
    6. 5.6. Rule.resourceQuery
    7. 5.7. loader
      1. 5.7.1. loader.pitch
      2. 5.7.2. css-loader
        1. 5.7.2.1. options.importLoaders
      3. 5.7.3. style-loader
      4. 5.7.4. babel-loader
        1. 5.7.4.1. 配置文件
        2. 5.7.4.2. @babel/core
        3. 5.7.4.3. plugins
          1. 5.7.4.3.1. @babel/plugin-transform-runtime
        4. 5.7.4.4. presets
          1. 5.7.4.4.1. @babel/preset-env
            1. 5.7.4.4.1.1. useBuiltIns
            2. 5.7.4.4.1.2. corejs
          2. 5.7.4.4.2. @babel/preset-react
        5. 5.7.4.5. 常用配置
      5. 5.7.5. postcss-loader
        1. 5.7.5.1. 配置文件
        2. 5.7.5.2. autoprefixer
        3. 5.7.5.3. postcss-import
        4. 5.7.5.4. postcss-cssnext
        5. 5.7.5.5. cssnano
      6. 5.7.6. react-hot-loader
      7. 5.7.7. eslint-loader
        1. 5.7.7.1. options.formatter
        2. 5.7.7.2. 配置文件
      8. 5.7.8. svg-sprite-loader
      9. 5.7.9. file-loader
      10. 5.7.10. url-loader
  6. 6. plugins
    1. 6.1. HtmlWebpackPlugin
      1. 6.1.1. title
      2. 6.1.2. filename
      3. 6.1.3. template
      4. 6.1.4. inject
      5. 6.1.5. minify
      6. 6.1.6. hash
      7. 6.1.7. cache
      8. 6.1.8. showError
      9. 6.1.9. chunks
      10. 6.1.10. excludeChunks
    2. 6.2. webpack.HotModuleReplacementPlugin
    3. 6.3. MiniCssExtractPlugin
    4. 6.4. webpack.ProvidePlugin
    5. 6.5. webpack.DefinePlugin
  7. 7. devtool
    1. 7.1. eval