使用webpack打包你的代码

Webpack是目前最流行的代码打包工具,大部分命令行打包工具都有借鉴Webpack的设计思想,三大框架都选用Webpack作为推荐的打包工具,大量开源项目也使用它来进行打包,所以学会如何使用它很有必要。

背景

本篇博文是承接先前的一篇博文webpack入门学习-基础篇的入门到进阶层次的文章。文章的核心还是关于如何配置和使用Webpack。并未涵盖Webpack的大部分高级特性。

目前 Webpack的最新版本为v4.7.0。

不过从4.x.x版本开始,为了改善大型项目Webpack配置困难的痛点,Webpack开始走部分技术推崇的约定大于配置的路线,而现有的比如Vue之类项目脚手架的webpack工具,仍然依赖的是3.x.x的版本。

所以本篇文章使用的webpack版本为3.6.0

下载

可以通过如下的方式来下载Webpack作为项目开发依赖:

开发依赖的包的信息,会保存在package.json文件的devDependencies字段里。

1
2
3
4
5
# NPM
$ npm install webpack@^3.6.0 --save--dev # npm i webpack@^3.6.0 -D

# Yarn
$ yarn add webpack@^3.6.0 --dev

查看当前Webpack依赖版本

1
2
3
4
$  .\node_modules\.bin\webpack -v

# Output
3.6.0 # 正确输出了版本

使用

Webpack需要通过命令行工具(Shell)来进行使用。

查看webpack命令行参数

可以看到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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
$ .\node_modules\.bim\webpack --help

# 输出结果
Hash: 91e97ca469d808640ddb
Version: webpack 3.6.0
Time: 52ms
Asset Size Chunks Chunk Names
bundle.js 2.57 kB 0 [emitted] main
[0] ./src/js/main.js 101 bytes {0} [built]
PS C:\Users\M S I\Desktop\webpack-demo> .\node_modules\.bin\webpack
No configuration file found and no output filename configured via CLI option.
A configuration file could be named 'webpack.config.js' in the current directory.
Use --help to display the CLI options.
PS C:\Users\M S I\Desktop\webpack-demo> .\node_modules\.bin\webpack --help
webpack 3.6.0
Usage: https://webpack.js.org/api/cli/
Usage without config file: webpack <entry> [<entry>] <output>
Usage with config file: webpack

Config options:
--config Path to the config file
[string] [default: webpack.config.js or webpackfile.js]
--config-name Name of the config to use [string]
--env Environment passed to the config, when it is a function

Basic options:
--context The root directory for resolving entry point and stats
[string] [default: The current directory]
--entry The entry point [string]
--watch, -w Watch the filesystem for changes [boolean]
--debug Switch loaders to debug mode [boolean]
--devtool Enable devtool for better debugging experience (Example:
--devtool eval-cheap-module-source-map) [string]
-d shortcut for --debug --devtool eval-cheap-module-source-map
--output-pathinfo [boolean]
-p shortcut for --optimize-minimize --define
process.env.NODE_ENV="production" [boolean]
--progress Print compilation progress in percentage [boolean]

Module options:
--module-bind Bind an extension to a loader [string]
--module-bind-post [string]
--module-bind-pre [string]

Output options:
--output-path The output path for compilation assets
[string] [default: The current directory]
--output-filename The output filename of the bundle
[string] [default: [name].js]
--output-chunk-filename The output filename for additional chunks
[string] [default: filename with [id] instead of [name] or [id] prefixed]
--output-source-map-filename The output filename for the SourceMap [string]
--output-public-path The public path for the assets [string]
--output-jsonp-function The name of the jsonp function used for chunk
loading [string]
--output-pathinfo Include a comment with the request for every
dependency (require, import, etc.) [boolean]
--output-library Expose the exports of the entry point as library
[string]
--output-library-target The type for exposing the exports of the entry
point as library [string]

Advanced options:
--records-input-path Path to the records file (reading) [string]
--records-output-path Path to the records file (writing) [string]
--records-path Path to the records file [string]
--define Define any free var in the bundle [string]
--target The targeted execution environment [string]
--cache Enable in memory caching [boolean]
[default: It's enabled by default when watching]

--watch-stdin, --stdin Exit the process when stdin is closed [boolean]
--watch-aggregate-timeout Timeout for gathering changes while watching
--watch-poll The polling interval for watching (also enable
polling) [string]
--hot Enables Hot Module Replacement [boolean]
--prefetch Prefetch this request (Example: --prefetch
./file.js) [string]
--provide Provide these modules as free vars in all modules
(Example: --provide jQuery=jquery) [string]
--labeled-modules Enables labeled modules [boolean]
--plugin Load this plugin [string]
--bail Abort the compilation on first error
[boolean] [default: null]
--profile Profile the compilation and include information in
stats [boolean] [default: null]

Resolving options:
--resolve-alias Setup a module alias for resolving (Example:
jquery-plugin=jquery.plugin) [string]
--resolve-extensions Setup extensions that should be used to resolve
modules (Example: --resolve-extensions .es6,.js)
[array]
--resolve-loader-alias Setup a loader alias for resolving [string]

Optimizing options:
--optimize-max-chunks Try to keep the chunk count below a limit
--optimize-min-chunk-size Try to keep the chunk size above a limit
--optimize-minimize Minimize javascript and switches loaders to
minimizing [boolean]

Stats options:
--color, --colors Enables/Disables colors on the console
[boolean] [default: (supports-color)]
--sort-modules-by Sorts the modules list by property in module
[string]
--sort-chunks-by Sorts the chunks list by property in chunk
[string]
--sort-assets-by Sorts the assets list by property in asset
[string]
--hide-modules Hides info about modules [boolean]
--display-exclude Exclude modules in the output [string]
--display-modules Display even excluded modules in the output
[boolean]
--display-max-modules Sets the maximum number of visible modules in
output [number]
--display-chunks Display chunks in the output [boolean]
--display-entrypoints Display entry points in the output [boolean]
--display-origins Display origins of chunks in the output
[boolean]
--display-cached Display also cached modules in the output
[boolean]
--display-cached-assets Display also cached assets in the output
[boolean]
--display-reasons Display reasons about module inclusion in the
output [boolean]
--display-depth Display distance from entry point for each
module [boolean]
--display-used-exports Display information about used exports in
modules (Tree Shaking) [boolean]
--display-provided-exports Display information about exports provided
from modules [boolean]
--display-optimization-bailout Display information about why optimization
bailed out for modules [boolean]
--display-error-details Display details about errors [boolean]
--display Select display preset (verbose, detailed,
normal, minimal, errors-only, none) [string]
--verbose Show more details [boolean]

Options:
--help, -h Show help [boolean]
--version, -v Show version number [boolean]
--json, -j Prints the result as JSON. [boolean]

打包生成信息

每次打包结束后,命令行就会输出如下信息:

1
2
3
4
5
6
Hash: 91e97ca469d808640ddb # 此次打包文件(main.js)的哈希值
Version: webpack 3.6.0 # 当前webpack的版本
Time: 53ms # 打包耗时
Asset Size Chunks Chunk Names
bundle.js 2.57 kB 0 [emitted] main # 打包生成文件名 大小等
[0] ./src/js/main.js 101 bytes {0} [built] # 打包生成的文件目录 及打包成功提示

每次执行打包命令时,如果文件未发生变化,那么打包文件的哈希值是不变的。

命令行工具使用

简单打包单个文件

1
$  .\node_modules\.bin\webpack .\src\js\main.js ./dist/bundle.js
  • 执行程序 .\node_modules.bin\webpack 因为是在命令行执行所以需要加上路径
  • 入口文件 指定一个入口文件,作为要打包的所有资源的注入点。
  • 生成文件 打包生成的最终文件,内部已经实现模块化,可为1个或者多个。

命令行监控文件并打包

此种打包模式可以通过在打包命令后添加--watch来实现,避免每次修改源文件后都需要手动执行打包命令。

通过此种方式运行命令后,命令行程序不会主动退出,每次修改源码后,命令行会显示新的打包结果相关信息。

1
$  .\node_modules\.bin\webpack .\src\js\main.js ./dist/bundle.js --watch

NPM Scripts使用

NPM Scripts是Nodejs为NPM提供的可以运行自定义脚本的一个配置字段。

package.json关键部分如下:

1
2
3
4
5
6
{
"scripts": {
"build": "webpack src/js/main.js dist/bundle.js",
"watch": "webpack src/js/main.js dist/bundle.js --watch"
}
}

要在watch的时候复用build的命令,可以这样实现npm run build -- --watch

项目中使用

项目中使用推荐通过编写webpack.config.js的方式与NPM Scripts结合使用。

Webpack会自动尝试加载项目根目录下的webpack.config.js文件,并导入其内配置好的所有配置,来执行打包。

当然我们可以通过--config参数来提供一个别的名字的配置文件。

接下来就记录一下如何进行简单且常用的webpack配置。

配置Webpack

在项目根目录下创建webapck.config.js文件。

webpack.config.js文件需要通过Commonjs规范导出一个JavaScript对象,这个对象将成为webpack的打包配置。

添加环境变量

通常情况下,我们需要将开发过程的环境分为:

  1. 开发环境
  2. 生产环境
  3. 测试环境

在不同的环境中,我们通常会对代码的打包有不同的需求。比如在开发调试过程中,未压缩过的代码可读性更好(当然现在有sourceMap)。但是我们会希望开发过程中项目打包时间比较快,我们可能并不需要执行部分工作。而且,可能以上3个环境,我们都会有不止一个。所以,我们希望环境是可以配置的。

实际应用中,我们可以通过在执行打包命令的时候以传入环境变量参数的方式来区分环境。

比如,在开发环境中,我们可以将表示环境的参数设置为development,生产环境设置为production,测试环境设置为test

因为操作系统的原因,我们需要通过不同的方式来传入参数,比如Mac OS或者Linux中通过NODE_ENV=env_name来实现,而在windows中,则需要通过set NODE_ENV=env_name来实现。

这个差异可以通过cross-env包来消除。

这个对象主要包括了如下字段:

Entry

文件打包的入口,所有的文件都要直接或间接地在这里产生依赖,才能被webpack程序运行时识别到。

配置示例:

1
2
3
module.exports = {
entry: './path_to_file/entry_name.js'
};

上面的配置会被webpack转化成如下的形式:

1
2
3
4
5
module.exports = {
entry: {
main: './path_to_file/entry_name.js'
}
};

最终生成的文件名[chunkName]为main

通常我们会将entry配置写为如下的形式:

1
2
3
4
5
6
module.exports = {
entry: {
app: './path_to_file/entry_name.js',
vendors: ['module1', 'module2', 'module3']
}
};
  • app用来打包页面的主js文件,它可能会随着产品的迭代经常更改。
  • vendors用来打包项目依赖,dependencies字段中一些可能长期不变的文件。

优点:可以方便利用文件的强制缓存,加快页面的加载速度,提升用户体验。

参数为数组格式的,会将多个文件打包到一起。

Output

Output用于告知webpack打包后生成文件的目录,注意只能指定一个输出配置。

要使用此配置,最少需要配置2个属性。

  1. 提供一个打包后生成的文件名格式。
  2. 提供一个文件生成的绝对路径

使用如下:

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

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

因为要使用绝对路径,所以我们需要引入Node内置的path包。

在配置filename的时候我们可以使用一些模板语法,如[name][chunkHash][hash],分别代表:

  • name 指定的输入文件名,打包前的文件名
  • hash 本次打包的hash值
  • chunkHash 本文件的hash值,使用chunkHash能利用文件打包的缓存。

Devtool

此字段用于配置如何生成sourcemap文件。

可配置参数有none#cheap-module-eval-source-map等。

详细内容与对比请戳

DevServer

用于配置webpack-dev-server插件,常用于开发环境中开启本地开发服务器。

依赖于webpack-dev-server包,需要另外下载:

1
2
3
$ npm install webpack-dev-server -D

$ yarn add webpack-dev-server --dev

此字段配置方式如下:

1
2
3
4
5
6
7
8
9
module.exports = {
devServer: {
contentBase: './dist/', // 服务器启动根目录
inline: true,
open: true, // 自动打开浏览器
hot: true, // 热更新模块
noInfo: true // 隐藏 更新文件时的打包信息 只在保存文件和重新打开时展现
}
};
有钱,任性!!!