webpack 基础

webpack 是前端必须掌握的技能

涉及的包

  • core

    • webpack
    • webpack-cli
    • webpack-dev-server
  • loader

    • style-loader
    • css-loader
    • sass-loader
    • file-loader
  • plugins

    • extract-text-webpack-plugin
    • extract-text-webpack-plugin

启动 webpack

webpack 启动时默认会加载启动目录下的 webpack.config.js 配置文件,也可以使用 –config 参数指定配置文件的位置

node_modules/.bin 目录下有 webpack webpack.cmd , 分别对应的是 bash 脚本和 cmd 脚本

所以可以在 bash cmd 中直接执行,执行的结果是 build 操作

1
sh node_modules/.bin/webpack
1
node_modules/.bin/webpack.cmd # .cmd 可省略

也可使用 npm run 命令执行。要先确保 package.json 文件中配置了 scripts

package.json
1
2
3
4
5
6
7
8
9
{
...
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "webpack --config webpack.config.js",
"dev": "webpack-dev-server --watch --hot"
}
....
}
1
2
npm start # webpack --config webpack.config.js
npm run dev # webpack-dev-server --watch --hot

配置解析

webpack 可以使用 js 对象进行详细的配置 。以 module.exports 的形式输出一个 Object 或 Function (并支持 Promise)

webpack 3.x 的配置还支持数组,同时执行多次不同构建,可一次行构建不同环境下的代码 [{}, fun, proFun]

Function 形式

1
2
3
4
5
6
7
8
9
10
/**
* 当使用命令行启动 webpack 时可指定参数(webpack -h 可列出所有支持的参数)。比如:
* webpack --env.production --env.version=1.2.0
* env 是当前运行时的 webpack 专属环境变量
* argv 是启动 webpack 时传入的所有参数
*/
module.exports = function(env = {}, argv) {
console.log(env) // { production: true, version: '1.2.0' }
return env.production ? prodConfig : devConfig
}

Object 形式

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
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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
module.exports = {
// webpack 使用的根目录 必须是绝对路径
context: __dirname,

/**
* js 入口文件
* 若值是 ./app.js 或 ['./main.js', './app.js'] 此时只输出一个 chunk app.js;
* 若值是 {app: './app.js', main: './main.js'} 输出两个 chunk app.js main.js
*/
// 输入
entry: ['./src/main.js'],

// 输出配置
output: {
/**
* 缺省值是 main.js
* 合并所有模块并输出到 main.js
* 使用模板和内置变量可以动态输出 chunk 名
* 内置变量有 id name hash chunkhash
* 默认 hash 长度 20 ,可指定,如 [hash:8] [chunkhash:16]
*/
filename: '[name].js',

// 运行过程中的 chunk 文件名
// chunkFileName:

// 输出文件存放的目录,必须是绝对路径,
path: path.resolve(__dirname, './dist'),

/**
* 发布到线上资源的 URL 前置,默认是相对路径 ''
* 比如设置为 blog ,则 index.html 中,资源文件路径要加前缀 blog. 如 blog/main.css
* https://cdn.example.com/ 发布到 CDN
*/
publicPath: 'blog',

/**
* 以何种方式输出轮子
* 造轮子的时候使用,缺省 var , 还支持 umd umd2 commonjs commonjs2 amd assign jsonp this window global
* 各个配置值轮子的使用方法: mylib; require('mylib'); this.mylib; window.mylib; global.mylib;
* 值为 commonjs2 时 library 配置无效
*/
libraryTarget: 'var',
// 轮子的名称 缺省 lib_code 浏览器开发者模式下资源服务器默认显示 webpack:// ,配置后显示 mylib
library: 'mylib',
// libraryTarget 值为 commonjs commonjs2 有效。指定要导出的子模块 String | Array
libraryExport: ['fn1', 'fn2],

// 获取异步加载的脚本的方式 use-credentials anonymous false
crossOriginLoading: false,
// jsonp 异步加载资源时回调函数名称
jsonpFunction: 'myJsonp',
// 生成的 map 文件名称
sourceMapFilename: '[file].map',
// 浏览器开发者工具源代码模块名称
devtoolModuleFilenameTemplate: 'webpack:///[resource-path]',
// 附加 chunk 的文件名称
chunkFilename: '[chunkhash].js',
// 包含有用的文件路径信息到代码里
pathinfo: false,
},
//
module: {
// 配置模块的读取和解析规则
rules: [ // 接受一个数组
/**
* loader 规则:
* 1.通过 test include exclude 用于过滤要配置的文件,均支持 String RegExp Array 为参数
* 2.通过 use 配置 loader
* 3.设置 include exclude parse noParse 以提高构建性能
*/
{
test: /\.scss$/, // RegExp
include: path.resolve(__dirname, 'src'), // 包含目录
exclude: path.resolve(__dirmane, 'node_modules') // 排除目录
/**
* 如下配置 4 个 loader
* loader 的 option 可以是 querystring,或写在 loader 对象 options 里
* loader 默认从右到左执行,enforce 选项可改变指定 loader 执行顺序
*/
use: ['style-loader', 'css-loader?sourceMap', 'postcss-loader' {
loader: 'sass-loader',
options: { indentedSyntax: true },
enforce: 'post',
// enforce: 'pre',
}
],
},
{
test: [/\.(gif|png|jpe?g)$/, /\.(eot|woff|ttf|svg|pdf)$/], // Array
include: [path.resolve(__dirname, 'src/images'), path.resolve(__dirname, '../static/images')],
use: ['file-loader'],
},
/**
* noPaser 配置那些文机不被解析处理,支持 RegExp [RegExp] function 参数
* parser 精确控制那些语法被解析处理
*/
{
test: /\.js$/,
use: ['babel-loader'],
// noParse: [/jQuery|chartjs/],
noParse: (content) => /jQuery|chartjs/.test(content),
parser: {
amd: false, // AMD
commonjs: false, // CommonJS
system: false, // SystemJS
harmony: false, // ES6 import/export
requireInclude: false, // require.include
requireEnsure: false, // require.ensure
requireContext: false, // require.context
browserify: false, // browserify
requireJs: false, // requirejs
}
}
]
},
// webpack 默认到 node_module 目录下寻找配置的 loader,可配置使用本地自定义的 loader
resolveLoader: {
// loader 所在目录
modules: ['node_modules', 'my_modules'],
// 入口文件后缀
extensions: ['.js', '.json'],
// 指定入口文件的字段
mainFields: ['loader', 'main']
},
// 如何寻找模块对应的文件
resolve: {
// 路径别名 还支持数组作为参数 [{name: './public', alias: 'pb', onlyModule: true}]
alias: {
@: 'src',
img: 'src/images'
},
/**
* 第三方库会针对不同的环境提供几份代码,定义在 package.json 文件中
* {
* "main": "lib/index.js", // 采用 ES5 语法的入口文件
* "jsnext:main": "es/index.js", // 采用 ES6 语法的入口文件
* }
* mainFields 用来配置优先使用的代码类型
*/
mainFields: ['jsnext:main', 'browser', 'main'],
/**
* 导入语句没有带文件扩展名后缀时, webpack 会自动加上后缀去尝试访问文件是否存在
* extensions 配置用于尝试的后缀,优先级按数组顺序
*/
extensions: ['.ts', '.js', '.json'],
/**
* webpack 默认到 node_modules 寻找第三方模块
* 但有时候我们依赖的模块在其它目录,比如 import mylib from 'src/lib_modules/mylib'
* 配置后就可以方便导入了。 import mylib from 'mylib'
*/
modules: ['node_modules', 'src/lib_modules'],
// 描述第三方模块文件的名称,也就是 package.json
descriptionFiles: ['package.json'],
// 导入语句强制带文件后缀
enforceExtension: true,
// 对于 node_modules 内文件的导入可以不带文件后缀
enforceModuleExtension: false,
// 是否跟随文件软连接去搜寻模块路径
symlinks: true
},
/**
* webpack 插件配置,接受一个数组,数组每一项是要使用的 plugin 的实例
* 但有时候我们依赖的模块在其它目录,比如 import mylib from 'src/lib_modules/mylib'
* 配置后就可以方便导入了。 import mylib from 'mylib'
*/
plugins: [
// 提取 css 到单独的 css 文件中,此插件使用 contenthash
new ExtractTextPlugin({}),
// 所有页面都会用到的公共代码被提取到 common 代码中
new CommonChunkPlugin({
name: 'common',
chunks: ['a', 'b']
})
],
// webpack-dev-server 的配置,webpack 不认识该配置,也可启动时在命令行指定参数
devServer: {
/**
* HTML5 History API 构建单页应用时开启
* 对任何路由请求都返回 index.html
* 如果应用由多个单页应用组成
*/
// historyApiFallback: true
historyApiFallback: {
rewrites: [
// 路由 /user 开头
{ from: /^\/user/, to: '/user.html' },
// 其它的返回主页
{ from: /./, to: '/index.html' },
]
},
/**
* 通过 DevServer 访问本地本地静态资源的规则
* 配置为 false 则访问不了本地静态资源, 只能访问内存中的构建结果
* 配置为 public 目录,http://localhost:8080/index.html 即访问了 public/index.html 文件
*/
// contentBase: false,
contentBase: path.resolve(__dirname, 'public'),
// HTTP 响应头中注入 header
headers: {
token: '3432ea-234efe-324aca-89793d',
bar: 'foo'
},
// 配置 DevServer 监听地址,默认值 127.0.0.0 只有本机可访问 --host 参数
host: '0.0.0.0', // 局域网内其它设备可访问
// 配合 --host 0.0.0.0 使用,关闭 host 检查
disableHostCheck: true,
// 端口默认 8080
port: 8080,
// 可访问的域名白名单
allowHosts: ['dev-host.com', '.zhulu.tech'],
/**
* 使用 HTTPS 协议
* HTTP2 Service Worker geolocation 需要运行在 HTTPS 上
* 可指定 HTTPS 证书
*/
// https: true
https: {
key: fs.readFileSync('https/server.key'),
cert: fs.readFileSync('https/server.crt'),
ca: fs.readFileSync('https/ca.pem'),
},
// '代理请求'
proxy: {
// 以 host + /api 开头的请求代理到 http://api.host.com/api 服务器上
'/api': 'http://api.host.com/api'
},
// 启用模块热替换功能 --hot 参数
hot: true,
// 构建变化后自动刷新网页实现实时预览
inline: true,
// 客户端日志等级,可取 none error warning info, 默认 info 级别
clientLogLevel: 'info',
// 启用 Gzip 压缩
compress: false,
// 启动后第一次构建完自动打开浏览器
open: true,
},
/**
* target
* 构建针对不同运行环境的代码
* web 默认,针对浏览器,所有的代码集中在一个文件里
* node 针对 node.js ,require 导入语句将保留,require 的内容不会打包到 chunk 里
* async-node 针对 node.js 异步加载代码
* node-webkit 针对 nw.js
* webworker 针对 webworker
* electron-main 针对 Electron 主线程
* electron-renderer 针对 Electron 熏染线程
*/
target: 'web',
// 构建的代码生成 Source Map 默认 false
devtool: false,
// 捕捉 webpack 构建的性能信息,需使用性能分析工具
profile: true,
// 启用缓存来提升构建速度
cache: true,
// 监听文件更新,重新编译。默认 false 。在使用 DevServer 时 默认 true
watch: true,
// 监听模式参数 watch 参数 为 true 生效
watchOptions: {
// 不监听的文件(夹),支持正则
ignored: /node_modules/,
// 监听到变化后 300ms 再执行编译
aggregateTimeout: 300,
// 每秒询问系统 1000 次 文件是否发生变化
poll: 1000
},
/**
* 告知 webpack 运行环境已经内置了那些全局变量
* webpack 不会把这些变量打包到代码中,而是直接使用
*/
externals: {
$: 'jQuery',
Vue: 'Vue'
},
// 输出文件性能检查,文件不能满足配置的条件时,控制台打印出提示
performance: {
// 性能问题提示类型 warning error false
hints: 'warning',
// 最大文件大小 bytes
maxAssetSize: 20,
// 最大入口文件大小 bytes
maxEntrypointSize: 100,
// 过滤出要检查的文件
assetFilter: (filename) => filename.endsWith('.js')
},
// 控制台输出日志信息
stats: {
assets: true,
colors: true,
errors: true,
errorDetails: true, // 显示详细的错误信息
hash: true
},
}

Comments

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×