基本搭建

1
npm init -y

一顿yes直接搭建好基本的node环境

1
yarn install webpack webpack-cli --save-dev

安装webpack, webpack-cli, 由于是开发环境需要,加上–save-dev

1
2
3
4
5
6
7
module.exports = {
entry: "./index.js",
mode: "development",
output: {
path: path.resolve(__dirname, 'dist'),
},
}

新建index.js, 新建webpack.config.js, webpack默认读取webpack.config.js导出的对象.

1
npx webpack

webpack默认读取根路径下的webpack.config.js文件, npm5.2之后提供了自动安装npx ,会自动查找当前依赖包中的可执行文件并执行


context

定义了打包的基础目录, 一旦设置, 那么接下来配置设置的根路径将会是配置的基础目录。

1
2
3
4
5
6
const path = require('path');

module.exports = {
//...
context: path.resolve(__dirname, 'app')
};

entry

文件打包的起点

  • 可以传入数组, 传入数组从前往后依次进行打包, 包名为main
  • 可以传入对象, 每个键作为打包的包名, 值为打包的资源路径或者打包的描述符
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    module.exports = {
    //...
    entry: './index.js'
    };

    // 也可以自定义包名
    module.exports = {
    //...
    entry: {
    app: './app.js',
    home: { import: './contact.js', filename: 'pages/[name][ext]' },
    about: { import: './about.js', filename: 'pages/[name][ext]' }
    }
    };
    多入口可以设置打包依赖, 减少包体积, 避免重复使用相同的包
    1
    2
    3
    4
    5
    6
    7
    module.exports = {
    //...
    entry: {
    app: { import: ['./app.js', './app2.js'], dependOn: 'react-vendors' },
    'react-vendors': ['react', 'react-dom', 'prop-types']
    }
    };
    可以动态引入, 将在make事件中触发
    1
    2
    3
    4
    module.exports = {
    //...
    entry: () => './demo' // () => new Promise((resolve) => resolve(['./demo', './demo2']))
    };

mode

打包模式分为developmentproduction

1
2
3
4
module.exports = {
//...
mode: 'development' // 'production'
};

可以在打包时决定

1
npx webpack --mode=development

如果我们需要在不同模式下进行定义不同的打包行为, 我们可以定义为函数, 使用方法如下。

开发环境打包文件webpack.base.js

1
2
3
module.exports = (options) => ({
// 这里注入基本配置
})

主打包文件webpack.dev.js

1
2
3
module.exports = require('./webpack.base.js')({
// 这里将开发环境打包需要的配置注入
})

通过配置进行打包, 选择对应的打包文件

1
webpack --config webpack.dev.js

output

打包的输出形态, 包括bundle和静态文件

filename

filename 是一个很常见的配置,就是对应于 entry 里面的输入文件。

这里可以使用可替换模板字符串的形式定义输出的打包文件名,浏览器存在缓存机制,如果名称相同,浏览器默认文件没有改变,这样打包文件名相同会使得获取文件出现问题,因此我们通过可替换模板字符串的形式定义输出的打包文件名。

  • 编译层
    • name: chunk文件名
  • chunk层
    • id: 内部chunk的id
    • chunkhash:这个chunk的hash值
  • 模块层
    • contenthash: 根据内容生成的hash
    • id:模块id
    • hash:模块的hash值
  • 文件层:
    • file: 文件名称和路径
    • query: 带前缀 ? 的 query
  • url层:
    • url

具体参照模板字符串替换

chunkFilename

chunkFilename 指未被列在 entry 中,却又需要被打包出来的 chunk 文件的名称。一般来说,这个 chunk 文件指的就是要懒加载的代码。

注意点: 默认文件输出为[id].js或者遵循output.filename的设置,如果output.filename设置了name, 则会被预先替换为id

可以参照这篇文章看到filename和chunkFilename的区别

path

打包文件路径

publicPath

外部资源路径,通常以/结尾, 默认参照路径为HTML页面所在的路径为准

例如如下配置,请求一个chunk就相当于请求/assets/1.chunk.js

1
2
3
4
5
6
7
module.exports = {
//...
output: {
publicPath: '/assets/',
chunkFilename: '[id].chunk.js'
}
};

library

组织打包的模块,库开发使用

libraryTarget

库打包引入的方式


Module

module对象用于模块的处理方式

rules

对应值为[Rule]数组

rules作用在于提供模块的创建规则,提供对应的loader和解析器(parser)

Rule

Rule是一个对象,这个对象分为三部分

  • 条件匹配: 条件匹配通过 testincludeexcluderesource 对资源进行匹配(这个资源指的是请求的资源),并且属性 issuerissuer 匹配, 这个 issuer 指的是请求者的文件绝对路径。是导入时的位置。
  • 应用规则: 对选中的文件使用 use 来应用 loader,按照从后往前的顺序应用, 并可以为loader传递参数。
  • 嵌套规则:父规则(文档这里没怎么理解, 在我看来是指一般的规则)、 ruleoneOf的顺序,可以通过enforce强制修改顺序。
1
2
3
4
5
6
7
8
9
10
11
12
13
module.exports = {
//...
module: {
rules: [ // 以下对象对应一个Rule
{
test: /\\.css$/, // loader1
},
{
test: /\\.js$/, // loader2
}
]
}
};
  • 注意: loader 顺序从右往左,从下往上执行,按照 pitching 顺序执行
    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

Rule.oneOf

一旦匹配,就使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
module.exports = {
//...
module: {
rules: [ // 以下对象对应一个Rule
{
test: /\\.css$/,
oneOf: [
{
resourceQuery: /inline/, // foo.css?inline
use: 'url-loader'
},
{
resourceQuery: /external/, // foo.css?external
use: 'file-loader'
}
]
}
]
}
};

Rule.use

提供一个 loader 数组, 从右往左进行匹配

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
module.exports = {
//...
module: {
rules: [
{
//...
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 1
}
},
{
loader: 'less-loader',
options: {
noIeCompat: true
}
}
]
}
]
}
};

Rule.exclude/include

排除/包括 loader 加载某个模块

Rule.parse

文件被 loader 执行后会转成一段js字符串, parse会将其转换为 AST 语法树,有了语法树就可以对代码为所欲为了,其中最重要的功能就是分析出这段代码依赖了哪些模块

Rule.sideEffect

模块是否包含副作用

Rule.type

设置匹配模块的类型,防止 webpack 自定义的行为发生,常用于自定义loader加载模块

Rule.resolve

模块解析可以在模块层被配置, Rule层面的 resolve 会覆盖顶层的 resolve配置

UseEntry

Rule 传入的形式,通常可以是对象或者一个函数, 一个必选的字符串形式的 loader 和一个可选的 option

函数形式则会接受一个加载模块的对象参数

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
// 对象形式
module.exports = {
//...
module: {
rules: [
{
loader: 'css-loader',
options: {
modules: true
}
}
]
}
};

// 函数形式
module.exports = {
//...
module: {
rules: [
{
loader: 'file-loader',
options: {
outputPath: 'svgs'
}
},
(info) => ({
loader: 'svgo-loader',
options: {
plugins: [{
cleanupIDs: { prefix: basename(info.resource) }
}]
}
})
]
}
};

noParse

不解析所传递的正则表达式匹配到的文件,这里特别注意不匹配的文件不能存在requireimport

1
2
3
4
5
6
module.exports = {
//...
module: {
noParse: /jquery|lodash/,
}
};

unsafeCache

缓存模块是否需要解析, 首先得保证缓存开启。缓存未开启,默认为false。缓存开启,默认会解析node_modules的模块。

1
2
3
4
5
6
7
module.exports = {
//...
module: {
cache: true,
// unsafeCache
}
};

Resolve

resolve的作用在于如何正确的识别模块,找到需要引入bundle的模块代码

alias

创建别名,使得引入方式更加简单

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

也可以使用 { '路径': false } 表示忽略路径的模块

resolve.alias 优先级高于其它模块解析方式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const path = require('path');

module.exports = {
//...
resolve: {
alias: {
Utilities: path.resolve(__dirname, 'src/utilities/'),
Templates: path.resolve(__dirname, 'src/templates/'),
},
},
};

// 原本方式
import Utility from '../../utilities/utility';

// 现阶段方式
import Utility from 'Utilities/utility';

Optimization

优化,会根据打包模式自动切换配置,也可以手动配置

minimize

开启表示需要压缩,需要配合 TerserPlugin 或其它在 optimization.minimizer 定义的插件压缩 bundle

minimizer

使用自定义压缩工具,覆盖默认压缩工具,通常使用 TerserPlugin

1
2
3
4
5
6
7
8
9
10
11
12
13
14
module.exports = {
optimization: {
minimizer: [
new TerserPlugin({
cache: true,
parallel: true,
sourceMap: true, // 如果在生产环境中使用 source-maps,必须设置为 true
terserOptions: {
// https://github.com/webpack-contrib/terser-webpack-plugin#terseroptions
}
}),
],
}
}

函数形式,形参为 compiler

1
2
3
4
5
6
7
8
9
10
module.exports = {
optimization: {
minimizer: [
(compiler) => {
const TerserPlugin = require('terser-webpack-plugin');
new TerserPlugin({ /* 你的选项 */ }).apply(compiler);
}
],
}
};

splitChunks

默认使用 webpack4默认的分块策略,具体手动配置参考 splitChunkPlugin

runtimeChunk

runtimeChunk作用在于将只含有runtime的入口文件单独打包一个文件, 所谓runtime就是指异步加载代码如import('./utils') => {}

emitOnErrors

编译时每当有错误时,会被 emit, 关键错误会被 emit 到代码中

todo

Plugins

DevServer

Devtool

Targets

watch

externals

performance

Node

stats对象

cache