-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathwebpack学习笔记.txt
619 lines (473 loc) · 25.8 KB
/
webpack学习笔记.txt
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
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
webpack是一个模块打包工具,通过依赖处理模块,并生成那些模块静态资源。
这里写图片描述
观察上图,webpack把所有的资源(js,css和图片等)都当做是模块——webpack中可以引用css,css中可以嵌入图片dataUrl,对于不同类型的资源,webpack有对应的模块加载器。webpack模块打包器会分析模块间的依赖关系,最后 生成了优化且合并后的静态资源。
为什么
当今网站正在变成一个web应用:一个页面包含的Javascript代码越来越多,结果客户端存在着大量的代码。通过模块系统划分代码到一个个小模块中,是管理大量代码的有效方式。目前有好几种方式用于实现模块和依赖关系定义:
<script>标签
简介:这是最基本的代码模块化方式,这种模块输出方式会暴露接口道一个全局对象,即window,需要通过全局对象来访问依赖的模块。
实例:
<script src="module1.js"></script> <script src="module2.js"></script> <script src="libraryA.js"></script> <script src="module3.js"></script>
缺点
不同模块容易发生 冲突
加载顺序依赖script标签的顺序
在一个大项目中,script标签列表可能变得很长,难以管理
CommonJS
简介:CommonJS使用同步的require方法加载依赖模块——模块可以通过给exports对象绑定属性或者给module.exports赋值来定义输出,
实例:
require("module"); require("../file.js"); exports.doStuff = function() {}; module.exports = someValue;
优点
服务端模块可以重用?
这种方式开发的模块有很多(NPM)
非常简单,很容易使用
缺点
阻塞式的调用不适用于网络(网络请求是异步的)
不能并发请求多个模块
实现
node.js
browserify
modules-webmake
wrep
AMD
简介:AMD是异步版本的模块CommonJS
实例:
require(["module", "../file"], function(module, file) { /* ... */ }); define("mymodule", ["dep1", "dep2"], function(d1, d2) { return someExportedValue; });
优点
实现了异步请求方式
并行加载多个模块
缺点
代码读写起来比较吃力
看起来像个变通方案
实现
require.js
curl
ES6
简介:EcmaScript6给javascript增加了一些语法,形成了另外一种模块系统
实例:
import "jquery"; export function doStuff() {} module "localModule" {}
优点
静态分析很简单?
ES标准,不会过时
模块确定了,要在客户端执行,必须从服务器传输到客户端。以下是两种极端的传输方式:
一个请求一个模块
优点:只传输请求的模块
缺点:多个模块需要多次请求,影响性能,应用启动慢
一次请求所有模块
优点:较少的请求消耗
缺点:不需要的模块也会被传输到客户端
上面两种模块加载方式比较极端,我们需要一个能够更加灵活地传输模块的方法。当在编译所有模块的时候,将整个系统划分成多个更小的模块集。各个模块集不会一开始就加载进来,只有在需要的时候才加载,所以初始化的时候并没有包含所有的模块代码。模块集的划分点取决于开发者,例如按功能划分,将该模块依赖的一个个模块当做一个模块集,当访问系统某项功能时,一次请求加载该项功能的模块集。
前面讨论完关于模块的定义与加载,我们发现为什么只提到了Javascript的模块系统,而前段还有许多其他的静态资源需要加载,如:样式表、图片、字体和html模板等。我们能不能像javascript一样使用require来加载指定的静态资源,例如:
require("./style.css"); require("./style.less"); require("./template.jade"); require("./image.png");
一个大点的项目,都会遇到上面的问题——模块划分和静态资源,目前已有的模块打包工具已经不适用了。而webpack作者曾尝试扩展已有的模块打包工具,但也是无法实现所有的构建目标。
webpack的目标:
将所有模块划分成一个个子模块集,在需要的时候才加载
保证较低的加载时间
将每一个静态资源都当做一个模块
能够以模块的方式集成第三方库
能够定制模块打包的每一部分
适用于大型项目
webpack的特性
模块划分
webpack不仅支持CommonJS——支持同步依赖,而且也支持AMD——支持异步依赖,也是模块划分点,形成一个新的模块集。
加载器
webpack只能加载本地的Javascript,通过一系列的加载器可以实现不同静态资源的加载。
智能编译
插件系统
webpack有着丰富的插件系统,大多数内部特性都是基于插件系统。
安装使用
安装
安装node.js——node.js自带有一个模块管理器npm
安装webpack
全局安装:npm install webpack -g,如果有将npm全局安装路径加入path,则可以直接在命令行输入webpack命令了
本地安装:npm install webpack --save-d,本地安装后的命令行工具在./node_modules/.bin/webpack.cmd,也可以在npm package.json里的scripts里引用webpack
用法
除了在命令行界面使使用webpack外,也可以直接在代码中使用webpack。
命令行界面
webpack --entry entry.js --output bundle.js
webpack --config webpack.config.js
webpack --config webpack.config.js -d:开发模式
webpack --config webpack.config.js -w:观察模式
webpack --config webpack.config.js -p:生成模式
node.js-api
简单示例
var webpack = require("webpack"); // returns a Compiler instance webpack({ // configuration }, function(err, stats) { // ... });
复杂示例
var webpack = require("webpack"); // returns a Compiler instance var compiler = webpack({ // configuration }); compiler.run(function(err, stats) { // ... }); // or compiler.watch({ // watch options: aggregateTimeout: 300, // wait so long for more changes poll: true // use polling instead of native watchers // pass a number to set the polling interval }, function(err, stats) { // ... });
常见问题:
webpack命令的--confgi选项是做什么的?
--config用来指定一个配置文件,代替命令行中的选项,从而简化命令。如果直接执行webpack的话,webpack会在当前目录查找名为webpack.config.js的文件。
…
简单示例
本例展示Hello World!,源代码如下:
index.html
<html> <head> <meta charset="utf-8"> </head> <body> <script type="text/javascript" src="bundle.js" charset="utf-8"></script> </body> </html>
entry.js
document.write(require("./content.js"));
content.js
module.exports = "Hello World.";
模块打包及运行:
执行命令webpack ./entry.js bundle.js,该命令会编译entry.js和content.js,并合并生成一个bundle.js。
在浏览器中打开index.html,显示:Hello World!.。
除了在webpack命令选项中指定模块入口文件和模块打包生成文件外,可以创建webpack.config.js来配置,具体配置如下代码所示。配置好后,在命令行中直接输入webpack(默认查找配置文件webpack.config.js)或者webpack --config webpack.config.js,这样效果同上面的命令webpack ./entry.js bundle.js。
module.exports = { entry: "./entry.js", output: { path: __dirname, filename: "bundle.js" } };
更多关于webpack.config.js的配置见下文。
模块详解
webpack同时支持CommonJS和AMD规范的模块编写方式, Hello World实例已经简单地演示了模块的定义和加载,下面我们开始学习webpack模块相关的api。
模块定义
module.exports
简介:CommJS规范的模块定义,module.exports指定的对象作为模块的输出,没有指定的话默认返回一个新对象,即{}。
实例:
module.exports = function doSomething() { // Do something };
总结:在加载module.exports格式定义的模块时,模块返回的是module.exports指定的对象
注意点:不可以在异步函数中使用module.exports
exports
简介:CommonJS规范的模块定义,exports是被输出的对象,如果已经指定了module.exports,则exports会被忽略
实例:
exports.someValue = http://blog.csdn.net/zhbhun/article/details/42; exports.anObject = { x: 123 }; exports.aFunction = function doSomething() { // Do something };
export label
简介:Labeld模块定义格式
实例:
export: var answer = 42; export: function method(value) { // Do something };
注意点:在异步函数中使用可能得不到想要的效果
define
简介:AMD规范的模块定义
语法:
define(value: !Function)
value 非函数值
define([name: String], [dependencies: String[]], factoryMethod: function(...))
name String 可选参数,表示模块名
dependencies String[] 可选参数,依赖的模块名数组
factoryMethod function 必填参数,该函数要求有个返回值,作为模块的输出
示例
define with value
define({ answer: 42 });
define with factory
define(["jquery", "my-module"], function($, myModule) { // Do something with $ and myModule. // Export a function return function doSomething() { // Do something }; });
总结:
define没有指定模块名称的时候,默认使用模块所在文件路径和名称作为模块名称,通常推荐使用这个名称就行了,不要再自己指定模块名称。
define函数可以指定依赖的模块,需要注意的是这些模块是同步加载的——模块本身和dependencies模块在同一个模块集里,即,webpack构建生成后放到同一个文件里。
…
注意点:不能在异步函数中使用
模块加载
require CommonJs
简介:CommonJS规范的require,同步加载指定的模块。webpack编译器会确保客户端执行到这行代码的时候,依赖的模块已经可用,无需向服务器发送请求——实际上webpack就是把需要加载的模块代码和require合并到同一文件里了,所以无需再发送请求获取模块了。
语法:require(dependency: String)
参数
dependency:需要加载的模块名
实例:HellowWorld实例的entry.js中存在这样一行代码document.write(require("./content.js"));,这便是一个commonJS规范的require。这里有两个模块:entry.js和content.js,执行webpack却只生成了一个文件bundle.js。
require label
简介:同CommonJS,require label也是同步加载指定模块,只是require语法不通罢了——webpack编译器会处理这种语法
语法:require: "dependency"
实例:
webpack自带实例labeled modules
自己写的测试实例labeled-modules
备注:注意require label需要借助插件webpack.dependencies.LabeledModulesPlugin,关于插件的使用,后续会介绍,暂时忽略,本节只是关注require label的写法
require AMD
简介:AMD规范的require,异步加载指定的模块——在需要的时候(代码执行到AMD require)才会加载依赖的模块。
语法:require(dependencies: String[], [callback: function(...)])
dependencies String[] 需要加载的模块名数组
callback function 回调函数,在所有模块加载后执行,dependencies指定的模块实例按参数定义的顺序传递给callback
实例:
// in file.js var a = require("a"); var d = require("d); require(["b", "d"], function(b, d) { var c = require("c"); }); /* This results in: * entry chunk - file.js - a - d * anonymous chunk - b - c */
总结:require AMD会影响webpack的模块打包结果,前面有提到AMD规范的模块依赖便是模块划分点。上例中执行webpack file.js bundle.js生成bundle.js和1.chunk.js,前者包含模块file.js、a.js和d.js,后者包含模块b.js和c.js。关于require AMD模块划分规则,如下所示:
dependencies的模块和callback中以CommonJS require的模块在之前都没有加载过,则webpack会将这些模块合并到同一个模块集里,生成一个chunk.js文件
dependencies的模块和callback中以CommonJS require的模块在之前有加载过,则加载过的模块会合并的之前的模块集里,本模块集不会包含这个模块,对于没有加载过的模块webpack处理同上
callback中以AMD require加载的模块按新的模块集处理
关于如何判断一个模块是否有加载过?webpack编译器自己会处理模块间的依赖关系来分析,一个比较复杂的例子可以参考CommonsJs, AMD and labeled modules mixed
require.ensure
简介:与require AMD类似,也是在需要的时候才会加载相应的模块。但不同的是,require.ensure在模块被下载下来后(模块还没被执行)便立即执行回调函数,另外require.ensure可以指定构建后chunk名,如果之前已有require.ensure指定了该名称,webpack会将这些模块统一合并到一个模块集里。
语法:require.ensure(dependencies: String[], callback: function([require]), [chunkName: String])
实例:
简单示例:
// in file.js var a = require("a"); require.ensure(["b"], function(require) { var c = require("c"); }); require.ensure(["d"], function() { var e = require("e"); }, "my chunk"); require.ensure([], function() { var f = require("f"); }, "my chunk"); /* This results in: * entry chunk - file.js - a * anonymous chunk - b - c * "my chunk" - d - e - f */
code-splitted-require.context
require.ensure VS require AMD
总结:除了require.ensure可以直接指定模块名外,require.ensure与require AMD一致,都可以进行代码划分,规则也一致。
require.include
简介: 确保模块是可用的(已经下载到客户端),但没有执行该模块(还没有得到模块输出),可用于优化模块在模块集的位置
语法:require.include(dependency: String)
实例:
// in file.js require.include("a"); require.ensure(["a", "b"], function(require) { // Do something }); require.ensure(["a", "c"], function(require) { // Do something }); /* This results in: * entry chunk - file.js - a * anonymous chunk - b * anonymous chunk - c Without require.include "a" would be in both anonymous chunks. The runtime behavior isn't changed. */
总结:略
其他API
require.resolve: 获取指定模块的id
module.id: 当前模块的id
require.cache: 模块缓存
require.context: 创建模块动态加载上下文,关于上下文的知识,后面章节会介绍
module.loaded
module.hot: 判断是否开启模块热加载,关于模块热加载的知识,后面章节会介绍
global
process
__dirname
__filename
__resourcequery
webpack_public_path
webpack_require
webpack_chunk_load
webpack_modules
require-resolveweak
webpack_hash
non_webpack_require
DEBUG
配置详解
webpack命令提供了一系列的命令选项用于控制模块打包——可通过webpack --help查看这些选项。当需要配置较多的选项时,会使webpack命令变得非常臃肿,如:webpack --entry entry.js --output-path ./build --ouput-file bundle.js。所以webpack提供了一个配置对象来简化项目模块打包,在运行webpack命令的时候,只要指定这个配置对象即可——webpack --config webpack.config.js,webpack.config.js会输出配置对象,例如:
module.exports = { entry: "./entry.js, output: { path: "./build", filename: "bundle.js" } };
观察上面的配置对象,可以发现配置对象本身就是一个模块,而不是JSON,我们可以在配置对象中随意的使用Javascript代码。除了在命令行界面中直接使用webpack,也可以在js代码中使用webpack api,例如:
var webpack = require("webpack"); webpack({ entry: "./entry.js, output: { path: "./build", filename: "bundle.js" }, callback);
webpack配置对象:
context
webpack处理entry选项时的基础路径(绝对路径),默认值为process.cmd(),即webpack.config.js文件所在路径
entry:
简介:webpack模块打包的入口,可以给entry赋字符串类型,数组类型和对象类型的值。
可选值:
字符串:字符串指定的模块在项目程序启动的时候加载
字符串数组:字符串数组指定的所有模块被当做一个模块集,在项目程序启动的时候都会加载,数组最后一个元素作为模块集输出
对象:webpack会打包多个模块集,对象的每个属性名作为模块集的名称,属性值可以是字符串和字符串数组。这个配置可用于实现非单页的程序,程序会有多个启动入口,具体实例可参考multiple entry points。
实例:
{ entry: { page1: "./page1", page2: ["./entry1", "./entry2"] }, output: { // Make sure to use [name] or [id] in output.filename // when using multiple entry points filename: "[name].bundle.js", chunkFilename: "[id].bundle.js" } }
output:option是个对象类型的值,option下的每个属性都可能会影响webpack模块打包结果。
output.path:字符串类型的值,指定webpack的输出文件路径——要求是个绝对路径
output.filenam: 字符串类型的值,指定模块集生成的文件名——在output.path指定的路径下,另外可以在字符串值中使用下列变量
[name]:代表模块集的名称,与entry配置有关,具体可自行测试
[hash]:代表编译hash值,与模块集的代码有关,如果模块集的代码有修改,hash值也会变,这个在生成环境里可以解决客户端的缓存问题
[chunkhash]:代表模块集名称的hash值,注意chunkhash与hash不能同时使用
output.chunckFileName:字符串类型,用于指定非程序入口模块集的文件名称——在output.path指定的路径下,另外可以在字符串值中使用下列变量
[id]: 代表模块集的id
[name]: 代表模块集的名称,和require.ensure的第三个参数,具体可以自行测试
[hash]: 代表编译hash值,与模块集的代码有关,如果模块集的代码有修改,hash值也会变,这个在生成环境里可以解决客户端的缓存问题
[chunkhash]: 代表模块集名称的hash值,注意chunkhash与hash不能同时使用
output.sourceMapFilename: SourceMaps的文件名称生成规则
output.devtoolModuleFilenameTemplate
output.devtoolFallbackModuleFilenameTemplate
output.devtoolLineToLine
output.hotupdateChunkFilename
output.hotUpdateMainFilename
output.publicPath
output.jsonpFunction
output.hotUpdateFunction
output.pathinfo
output.pathinfo
output.library
output.libraryTarget
output.sourcePrefix
output.crossOriginLoading
module:用于配置模块加载插件,后面的章节再具体介绍
resolve
externals
target
bail
profile
cache
watch
watchOptions.aggregateTimeout
watchOptions.poll
debug
devtool
node
amd
loader
recordsPath, recordsInputPath, recordsOutputPath
plugins: 插件
注:不得不说webpack的强大,提供如此之多的配置选项,由于精力有限,后续再慢慢维护完善各个配置选项的说明。
加载器
加载器简介
加载器在webpack中作为资源(不只是javascript,包含其他静态资源)转换器,它本身是个可以运行在node.js环境中的函数。加载器根据参数对源文件进行处理,返回新的源文件,例如:在webpack中使用加载器处理CoffeeScrip和JSX——CoffeeScript是JavaScript的转译语言,JSX是Javascript的超集,都不能直接运行在浏览器中,但加载器会将他们编译成普通的javascript代码。
加载器的特性:
加载器可以链接使用——多个加载器作用在一个资源上,按编写顺序使用加载器
加载器可以是同步的,也可以是异步的?
加载器运行在nodejs环境里,可以做node.js所能做的一切事情——开发自己的加载器时,可以体会到
加载器可以接受参数,作为加载器的配置来影响加载器对资源的转换
加载器可以通过npm来安装和发布——上面提到了加载器本身就是个运行在node.js环境的函数,所以可以作为node.js包发布在npm上
加载器可以读取webpack的配置
加载器可以结合webpakc的插件使用——插件在后面章节介绍
安装加载器
简介中提到了加载器是发布在npm上,作为一个模块使用——输出一个转换函数。所以我们可以通过npm来安装管理加载器,例如:
npm install xxx-loader --save-dev
或者
npm install xxx-loader --save
作为约定,加载器的命名通常为xxx-loader——xxx是加载器的名字,例如:json-loader。我们既可以使用xxx-loader来引用加载器,也可以直接使用xxx。
加载器使用
在webpack中使用加载器有三种方式:
1. 直接在require中引用——不推荐
2. 在webpack.config.js中配置——推荐
3. 通过命令选项配置——不推荐
除了一些特殊情况在require中引用加载器外,我们推荐使用第二种方式来使用加载器——不需要解释,在后面的使用中很容易体会到。
下面简单介绍下这三种方式的用法:
require
说明:可以在require语句(或者define,require.ensure等等)中指定加载器,加载器名称与模块名称通过!分割
实例:
require("./loader!./dir/file.txt"); // => uses the file "loader.js" in the current directory to transform // "file.txt" in the folder "dir". require("jade!./template.jade"); // => uses the "jade-loader" (that is installed from npm to "node_modules") // to transform the file "template.jade" require("!style!css!less!bootstrap/less/bootstrap.less"); // => the file "bootstrap.less" in the folder "less" in the "bootstrap" // module (that is installed from github to "node_modules") is // transformed by the "less-loader". The result is transformed by the // "css-loader" and then by the "style-loader".
总结:第一个require使用了当前目录下的加载器./loader.js;第二个require使用了npm安装的加载器jade-loader;第三个加载器使用了加载器的链接特性,首先会使用less-loader处理源文件,处理后的源文件交给css-loader处理,最后交给style-loader——less-loader转换less源文件为普通的css文件,css-loader处理css文件中的url(由于webpack构建的代码会合并统一放在一个路径下,所以要处理下css引用的资源,否则在实际运行时,无法正确找到资源的位置),style-loader会处理css文件,通过js代码将插入到html页面中
配置文件
说明:学习require直接引用加载器后,我们发现很多资源使用的加载器是一样的。webpack提供了配置选项,让我们可以在配置文件中通过正则表达式来匹配资源,指定某一类资源要使用的加载器
实例:
{ module: { loaders: [ { test: /\.jade$/, loader: "jade" }, // => "jade" loader is used for ".jade" files { test: /\.css$/, loader: "style!css" }, // => "style" and "css" loader is used for ".css" files // Alternative syntax: { test: /\.css$/, loaders: ["style", "css"] }, ] } }
命令行选项
webpack --module-bind jade --module-bind 'css=style!css'
加载器参数
加载器提供了一些参数来控制加载器对资源的转换,添加参数的方式类似于url链接中的查询参数。
- require
require("url-loader?mimetype=image/png!./file.png");
- 配置
`{ test: /\.png$/, loader: "url-loader?mimetype=image/png" }` 或者 ``` { test: /\.png$/, loader: "url-loader", query: { mimetype: "image/png" } } ```
命令行选项
webpack --module-bind "png=url-loader?mimetype=image/png"
加载器库
webpack有着丰富的加载器实习,可以参考list-of-loaders来查找你需要的加载器。
简单实例
略
插件
插件简介
webpack有丰富的插件接口,使得webpack变得更加灵活。webpack内部集成了一些插件,另外在npm上也有发布一些第三方的插件,当然你可以自己根据webpack的接口,开发属于自己的插件。
内建插件
webpack本身提供了一些插件供我们使用,如webpack.optimize.DedupePlugin——查找相等或近似的模块,避免在最终生成的文件中出现重复的模块。下面就以webpack.optimize.DedupePlugin为例展示webpack内部插件的使用:
webpack.config.js
var webpack = require("webpack"); var path = require("path"); module.exports = { entry: "./example.js", output: { path: path.join(__dirname, "js"), filename: "output.js" }, plugins: [ new webpack.optimize.DedupePlugin() ] }
example.js
var a = require("./a"); var b = require("./b"); a.x !== b.x; a.y !== b.y;
a/index.js
module.exports = { x: require("./x"), y: require("./y"), z: require("../z") }
a/x.js
module.exports = {"this is": "x"};
a/y.js
module.exports = {"this is": "y", "but in": "a"};
b/index.js
module.exports = { x: require("./x"), y: require("./y"), z: require("../z") }
b/x.js
module.exports = {"this is": "x"};
b/y.js
module.exports = {"this is": "y", "but in": "b"};
z.js
module.exports = {"this is": "z"};
js/output.js
总结
观察上面的示例代码,a和b下都有模块x,且他们的代码是一样的。如果直接在命令行运行webpack example.js output.js,最终的生成文件会有两个模块x。再观察上面的webpack配置文件webpack.config.js,最后增加了plugins: [new webpack.optimize.DedupePlugin()],如果使用这个配置运行webpack构建,可以发现最后的生成文件js/ouput.js里只有个模块x,这个就是插件webpack.optimize.DedupePlugin的作用。
更多内建插件的使用请参考LIST OF PLUGINS
其他插件
除了内建插件外,还有一些webpack插件发布在npm上了,我们可以通过npm命令来安装,例如:
npm install --save-dev extract-text-webpack-plugin——extract-text-webpack-plugin是用于将某些类型的模块,如css,单独提取到文件的插件,具体如何使用该插件可以参考该项目的主页extract-text-webpack-plugin。
如果想要查找更多的webpack插件,可以在npm上利用webpack-plugin关键字来搜索,即webpack-plugin。
自定义插件
Plugins-api
开发工具
webpack-dev-server
认识webpack-dev-server
webpack-dev-server是一个轻量级的node.js服务器,同webpack,webpack-dev-server也可以分析模块的依赖关系进行打包——webpack命令的所有选项对webpack-dev-server命令都有效。但不同的是,webpack-dev-server不会在本地目录下生成目标文件,而是放在内存里,通过访问服务器相应地址获取。另外,webpack-dev-server自带有一个轻量级的runtime,浏览器可以通过Socket.IO与服务器连接,服务器向客户端浏览器送编译状态信息,然后客户端/浏览器根据这些信息做相应的处理,如:刷新,模块热替换等。
简单示例
项目结构
app/
main.js
build/
test/
index.html
webpack.cong.js
webpack.config.js
module.exports = { entry: { app: ["./app/main.js"] }, output: { path: "./build", publicPath: "/assets/", filename: "bundle.js" } };
app/main.js
document.write("Hello World!")
test/index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <script src="assets/bundle.js"></script> </body> </html>
命令行运行webpack-dev-server --content-base test/
访问项目路径build发现并没有生成相应的构建文件
浏览器访问localhost:8080/,观察运行结果
分析总结
webpack-dev-server --content-base test/中的--content-base指定html等静态资源的位置
webpack.config.js中的output.publicPath指定webpack模块打包后在服务器的路径
webpack-dev-server命令行接口
所有的webpack命令选项对webpack-dev-server命令都有效,但是没有默认的output参数。除此之外,webpack-dev-server还有一些额外的选项:
- --content-base: 可以是文件、目录或者url,表示静态资源的基础路径
- --quiet: 布尔值,控制是否要在控制台输出所有信息
- --colors: 为控制台输出信息增加一些颜色
- --no-info: 禁止输出一些无聊的信息?
- --host: 主机名或IP地址
- –port: 端口号
- –inline: 布尔值,表示是否要将webpack-dev-server runtime集成到模块打包文件里,可以实现浏览器与服务器的通信
- --hot: 布尔值,表示增加HotModuleReplacementPlugin插件,且将服务器切换到热模式中。注意点:不要再额外添加HotModuleReplacementPlugin
- --https: 布尔值,表示webpack-dev-server是否开启HTTPS协议
webpack-dev-server API
http://webpack.github.io/docs/webpack-dev-server.html#api
最佳实战
在真实的项目开发中,前端需要与后台服务器交互——传输业务数据,这里通常会有一个后台服务器来向前端提供业务数据接口,我们也是在浏览器中访问这个后台服务器来使用功能的。这样的话,我们就需要将webpack生成的静态资源拷贝到我们项目的后台服务器下。如果是在最后发布的话,这样没有什么问题,但在实际开发中,每次调整代码都需要这么做,开发人员在就放弃使用webpack了。
当然,webpack已经为我们解决了这个问题,我们只需要同时运行后台服务器和webpack-dev-server——后台服务器的HTML页面包含指向webpack-dev-server静态资源的script标签,而webpack-dev-server只是充当前端静态资源的服务器。
示例:
- webpack.config.js
module.exports = { entry: { app: ["./app/main.js"] }, output: { path: "./build", publicPath: "http://localhost:9090/", filename: "bundle.js" } };
webpack-dev-server启动命令: webpack-dev-server --config webapck.config.js --host localhost --port 9090 --inline
后台服务器的index.html包含: <script src="http://localhost:9090/bundle.js">
假设后台服务器地址是http://localhost:8080,则在浏览器中访问: http://localhost:8080/index.html
注意点:
- 为了保证总是(为了非入口模块)向webpack-dev-server发送请求来获得静态资源,必须向output.publicPath提供一个完整的URL——如果只是个目录路径的话,非入口模块静态资源的请求会向后台服务器发送,而不是webpack-dev-server
- 为了连接webpacl-dev-server和它的runtime最好在webpack-dev-server命令后加上选项--inline
常见问题
webpack-dev-server的运行配置方式?
命令行选项,参考 webpack-dev-server-cli ,此外webpack-dev-server在命令行里也可以使用webpack的配置选项来控制服务器构建
nodejs自定义脚本运行,参考api
webpack.config.js的 devServer选项,参考 devserver
可参考实例 run-option-mode
webpack-dev-server的运行选项content-base与publicPath的区别?
webpack-dev-server是一个 轻量级 服务器, content-base指定服务静态资源(html等)的路径。webpack-dev-server构建后的代码是放在内存里的,并通过publicPath指定的路径发布,如果没有指定的话在服务器地址的根路径下(如:假设host为127.0.0.1,port为8080,output.filename为bundle,则构建后的代码文件地址是 http://127.0.0.1:8080/bundle.js )
hot模式与inline模式?
webpack-dev-middleware
webpack-dev-middleware
koa-webpack-dev
koa-webpack-dev
工具集成
grunt
gulp
bower
karma
Todo
源码分析
Todo
最佳实战
模块划分
优化
Todo
常见问题
不支持的模块(如jQuery插件)格式编译
shimming-modules
Managing Jquery plugin dependency in webpack
echarts整合问题
Webpack #1407
webpack能加载echarts吗?
请原生支持webpack #1632
webpack集成echarts #1625
hushicai/echarts-develop
参考文献
社区收藏
StackOverflow Webpack Tag
segmentfault Webpack Tag
工具对比
Browserify vs. Webpack
中文文档
Webpack 入门指迷
深入浅出React(二):React开发神器Webpack
webpack这个js模块管理器(module bundler)怎么样?
webpack前端模块加载工具
Webpack 和 Gulp 构建伪命令行项目
最佳实践
Getting started with webpack
newtriks/generator-react-webpack
book-of-modern-frontend-tooling-webpack
Tutorial: Setting Up a Single Page React Web App with React-router and Webpack
Configure reactjs with webpack and grunt
Single Page Modules with Webpack
项目模板
ThomasDeutsch/react-webpack
jaketrent/html-webpack-template
magicdrive/webpack-project-template
Google webpack project structure
Webpack + React
react-webpack-cookbook
Creating a workflow with WebPack
React Hot Loader
基于ES6,使用React、Webpack、Babel构建模块化JavaScript应用
轻松入门React和Webpack
The React.js Way: Getting Started Tutorial
轻松入门React和Webpack
webpack前端模块加载工具
How-to setup Webpack on an ES6 React Application with SASS?
轻松入门React和Webpack
Webpack with LESS and React
Building modular javascript applications in ES6 with React, Webpack and Babel
基于ES6,使用React、Webpack、Babel构建模块化JavaScript应用
前端革命,革了再革:WebPack