Webpack 配置指南

webpack 是什么?

webpack是一个前端的模块化打包工具,主要用于将各种前端静态资源打包成一个或者多个的 Bundle ,方便引用。

安装

这里假设你已经对 Node 构建工具 如 npm,yarn 等工具有所了解。

node 版本,v10.2.1
npm 版本,5.6

初始化目录

使用 Linux 命令创建一个空目录,然后使用 npm 初始化。

1
2
3

mkdir project11 && cd project11 //创建并进入

使用 npm 初始化当前目录,生成一个 package.json 包含这个项目的相关信息。

1
2
3

npm init -y // -y 相当于一路yes 过去,使用默认配置

成功则会在当前目录自动生成一个配置项,package.json。

比如我的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14

{
"name": "project11",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}

这个配置项记录了这个项目的相关信息,包括项目名,版本,描述,还有安装的node模块等。

安装webpack

由于 webpack 4.0 以后,把很多的构建都转移到了 webpack-cli 这个指令上,所以 安装 webpack 还要一起安装 webpack-cli 。

1
2
3

npm i webpack webpack-cli -D

安装结果会有一些警告信息,这是因为我们的 package.json 信息没有填写完整,但这不妨碍我们接下来的工作。

1
2
3
4
5
6
7
8
9
10
11
12

$ npm i webpack webpack-cli -D
npm notice created a lockfile as package-lock.json. You should commit this file.
npm WARN project11@1.0.0 No description
npm WARN project11@1.0.0 No repository field.
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@1.2.4 (node_modules\fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.2.4: wanted {"os":"darwin","arch":"any"} (current: {"os":"win32","arch":"x64"})

+ webpack-cli@3.0.3
+ webpack@4.12.0
added 396 packages in 27.621s

安装后,package.json 里面,就出现了 webpack 相关的信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

{
"name": "project11",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"webpack": "^4.12.0",
"webpack-cli": "^3.0.3"
}
}

创建dist和src目录,和 webpack配置项

src 目录存放我们的源代码,也就是我们编写的代码
dist 存放 webpack 打包生成的文件,我们最后引用,也是这个。

1
2
3

mkdir dist && mkdir src

配置 webpack.config.js

很多时候,我们需要装的东西很多,比如 React 要装 react-dom 和 react 这两个依赖项目,当然可以用命令行去装。
比如:

1
2
npm i react react-dom

但是,很多时候,这样既难记,又繁琐。

所以不管是,package.json 还是 webpack.config.json 都是为了解决这个问题出现的。

1
2
3

vi webpack.config.json

写入配置信息

1
2
3
4
5
6
7
8
9
10
11

const path = require('path');

module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
}
};

上面就配置了webpack的入口信息,和生成的出口信息,名字,目录等。

现在我们看到的项目目录是这样的。

1
2
dist          package.json       src
node_modules package-lock.json webpack.config.js
编写代码

我们在 src 目录新建一个 index.js 文件。写一个函数。

1
2
3
4
5

(function(age){
alert(age);
})(22);

注意,这里要将相关的依赖关系捆绑在一起,要安装 lodash 库。

1
2
3

npm i --save lodash

在 index.js 中引入

1
2
3
4
5
6
7

import _ from "lodash";

(function(age){
alert(age);
})(22);

在dist目录下,创建一个 index.html 文件。编写如下代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<h1>hello webpack!</h1>
</body>
<script src="bundle.js"></script>
</html>

此处的bundle.js是 webpack 生成的,只需要保持与 webpack.config.js 里的 output 的 file name 一致即可。

执行打包命令

1
2
3

npx webpack

执行结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

npx: installed 1 in 13.391s
The "path" argument must be of type string. Received type undefined
G:\webpack\project11\node_modules\webpack\bin\webpack.js
Hash: 85a63ecdb30268885edd
Version: webpack 4.12.0
Time: 3153ms
Built at: 2018-06-11 20:48:48
Asset Size Chunks Chunk Names
bundle.js 70.3 KiB 0 [emitted] main
[0] (webpack)/buildin/module.js 497 bytes {0} [built]
[1] (webpack)/buildin/global.js 489 bytes {0} [built]
[3] ./src/index.js 68 bytes {0} [built]
+ 1 hidden module

WARNING in configuration
The 'mode' option has not been set, webpack will fallback to 'production' for this value. Set 'mode' option to 'development' or 'production' to enable defaults for each environment.
You can also set it to 'none' to disable any default behavior. Learn
more: https://webpack.js.org/concepts/mode/

此时即可打开 浏览器 造访这个 index.html 文件。观察结果。

这里有个错误,是因为我们没有设定它的模式,这里我们将它改成 production 。

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

module.exports = {
mode:'production',
entry:'./src/index.js',
output:{
filename:'bundle.js',
path:path.resolve(__dirname,'dist')
}

}

在执行打包,就正常了。

1
2
3
4
5
6
7
8
9
10
11
12
13

$ npx webpack
npx: installed 1 in 1.409s
The "path" argument must be of type string. Received type undefined
G:\webpack\project13\node_modules\webpack\bin\webpack.js
Hash: 30b1b5471a70602e5c0c
Version: webpack 4.12.0
Time: 88ms
Built at: 2018-06-12 09:02:48
Asset Size Chunks Chunk Names
bundle.js 939 bytes 0 [emitted] main
[0] ./src/index.js 43 bytes {0} [built]

修改 private

为了防止意外发布,我们修改 package.json 文件的 private 属性为 true,并删去 main 的配置。

最终:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

{
"name": "project11",
"version": "1.0.0",
"description": "",
"private": true,
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"webpack": "^4.12.0",
"webpack-cli": "^3.0.3"
},
"dependencies": {
"lodash": "^4.17.10"
}
}

多文件生成

在这里的 src 目录中,我们只编写了一个js文件,那么如果有多个JS文件呢?

这里就要使用插件了。

1
2
3

npm i html-webpack-plugin -D

在 webpack.config.js 中引入。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

const path = require('path');
var htmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
mode:'production',
entry:{
main:'./src/index.js',
a:'./src/a.js'
},
output:{
filename:'[name]-[chunk-hash].js',
path:path.resolve(__dirname,'dist')
},
plugins:[
new htmlWebpackPlugin({
template: 'index.html'
})
]

}

上面我们还做了一件事情,就是在出口处,定义了两个 entry 文件。出口处,filename 以文件名加 chunk-hash 作为动态变化的文件名参数。plugins 指定了声明模板在指定文件加载。

打包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

npx: installed 1 in 2.477s
The "path" argument must be of type string. Received type undefined
G:\webpack\project13\node_modules\webpack\bin\webpack.js
Hash: 5c57889e71677e4dd158
Version: webpack 4.12.0
Time: 339ms
Built at: 2018-06-12 11:08:39
Asset Size Chunks Chunk Names
a-[chunk-hash].js 939 bytes 0 [emitted] a
main-[chunk-hash].js 940 bytes 1 [emitted] main
index.html 447 bytes [emitted]
[0] ./src/a.js 43 bytes {0} [built]
[1] ./src/index.js 43 bytes {1} [built]
Child html-webpack-plugin for "index.html":
1 asset
[0] (webpack)/buildin/module.js 497 bytes {0} [built]
[1] (webpack)/buildin/global.js 489 bytes {0} [built]
[3] ./node_modules/html-webpack-plugin/lib/loader.js!./index.html 533 bytes {0} [built]
+ 1 hidden module

查看 dist 目录。

1
2
3

'a-[chunk-hash].js' bundle.js index.html 'main-[chunk-hash].js'

生成的html文件,已经自动引入。

1
2
3
4
5
6
7
8
9
10
11
12
13
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>

<script type="text/javascript" src="main-[chunk-hash].js"></script><script type="text/javascript" src="a-[chunk-hash].js"></script></body>
<script src="dist/bundle.js"></script>
</html>

但是,很显然,这不符合我们开发时候的各种代码分类存放的习惯,那么可以这样改动,webpack.config.js 文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

const path = require('path');
var htmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
mode:'production',
entry:{
main:'./src/index.js',
a:'./src/a.js'
},
output:{
filename:'js/[name]-[chunk-hash].js',
path:path.resolve(__dirname,'dist')
},
plugins:[
new htmlWebpackPlugin({
filename:'index-[hash].html',
template: 'index.html'
})
]

}

我们在 output 的 filename 属性上,增加了js目录,这样一来,所有生成的js文件,都会在js目录中。
还可以指定,js标签引入的标签区域。

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

const path = require('path');
var htmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
mode:'production',
entry:{
main:'./src/index.js',
a:'./src/a.js'
},
output:{
filename:'js/[name]-[chunk-hash].js',
path:path.resolve(__dirname,'dist')
},
plugins:[
new htmlWebpackPlugin({
filename:'index-[hash].html',
template: 'index.html',
inject:'head'
})
]

}

````

html文件

```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script type="text/javascript" src="js/main-[chunk-hash].js"></script><script type="text/javascript" src="js/a-[chunk-hash].js"></script></head>
<body>

</body>
<script src="dist/bundle.js"></script>
</html>

这里我们做一个调整,先把 webpack.config.js 改成

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

const path = require('path');
var htmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
mode:'production',
entry:{
main:'./src/index.js',
a:'./src/a.js'
},
output:{
filename:'js/[name]-[hash].js',
path:path.resolve(__dirname,'dist')
},
plugins:[
new htmlWebpackPlugin({
filename:'index.html',
template: 'index.html',
inject:'head',
title:'Hello My Name Is Wanhai!'
})
]

}

模板语法

这里假使我们要在最后生成的 html 里,统一它的比如 title 的值,那么可以这样办。
我们前面的 plugins 已经新增加了 title 属性,并将值设定为指定的字符串数据。

怎么使用呢?

在最外层的 index.html 文件写入如下

1
2
3
4
5
6
7
8
9
10
11
12
13
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title><%= htmlWebpackPlugin.options.title%></title>
</head>
<body>

</body>
</html>

title 里面的就是模板语法,我们在这一步之后,再执行打包。

就可以在 dist 目录生成的 index.html 文件中,看到

1
2
3
4
5
6
7
8
9
10
11
12
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Hello My Name Is Wanhai!</title>
<script type="text/javascript" src="js/main-ccb66b3a0848585d05bd.js"></script><script type="text/javascript" src="js/a-ccb66b3a0848585d05bd.js"></script></head>
<body>

</body>
</html>

在 plugins 的设置项中,我们甚至可以直接写 js 。

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

const path = require('path');
var htmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
mode:'production',
entry:{
main:'./src/index.js',
a:'./src/a.js'
},
output:{
filename:'js/[name]-[hash].js',
path:path.resolve(__dirname,'dist')
},
plugins:[
new htmlWebpackPlugin({
filename:'index.html',
template: 'index.html',
inject:'head',
title:'Hello My Name Is Wanhai!',
date: new Date()
})
]

}

在 外层的 index.html 模板中输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title><%= htmlWebpackPlugin.options.title%></title>
</head>
<body>
<h1><%= htmlWebpackPlugin.options.date %></h1> //Tue Jun 12 2018 13:08:16 GMT+0800 (中国标准时间)
</body>
</html>

现在我们的所有代码,都是在本地,但是在线上的话呢,这个URL肯定不一样。那么怎么处理呢?

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

const path = require('path');
var htmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
mode:'production',
entry:{
main:'./src/index.js',
a:'./src/a.js'
},
output:{
filename:'js/[name]-[hash].js',
path:path.resolve(__dirname,'dist'),
publicPath: 'http://wuwanhai.com/'
},
plugins:[
new htmlWebpackPlugin({
filename:'index.html',
template: 'index.html',
inject:'head',
title:'Hello My Name Is Wanhai!',
date: new Date()
})
]

}

添加 publicPath 即可。填写我们的域名地址。

多页面生成

一个一个的生成文件很不方便,而且,不同的问题,生成,引入也是很麻烦的事情,那么可以用 webpack 自动生成

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

const path = require('path');
var htmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
mode: 'production',
entry: {
a: './src/a.js',
b: './src/b.js',
C: './src/c.js'
},
output: {
filename: 'js/[name]-[hash].js',
path: path.resolve(__dirname, 'dist'),
publicPath: 'http://wuwanhai.com/'
},
plugins: [
new htmlWebpackPlugin({
filename: 'a.html',
template: 'index.html',
inject: 'body',
title: 'Hello My Name Is a.html',
date: new Date(),
excludeChunks: ['b', 'c']
}),
new htmlWebpackPlugin({
filename: 'b.html',
template: 'index.html',
inject: 'body',
title: 'Hello My Name Is b.html',
date: new Date(),
excludeChunks: ['a', 'c']
}),
new htmlWebpackPlugin({
filename: 'c.html',
template: 'index.html',
inject: 'body',
title: 'Hello My Name Is c.html',
date: new Date(),
excludeChunks: ['b', 'a']
})
]

}

这样就可以自动生成这个文件和文件的引入。

这里其实已经可以发现了,webpack的所有功能都可以通过配置 webpack.config.js 文件生成。

这里是我写的例子源码

访问 webpack-demo