转载自:http://www.jb51.net/article/117490.htm
本篇主要介绍:如何自动构建入口文件,并生成对应的output;公共js库如何单独打包。
多入口文件,自动扫描入口。同时支持SPA和多页面型的项目
公共js库如何单独打包。
基础结构和准备工作
以下示例基于上一篇进行改进,上一篇项目源码
目录结构说明
1
2
3
4
5
6
7
8
9
10
11
12
13
|
.
├── package.json # 项目配置
├── src # 源码目录
│ ├── pageA.html # 入口文件a
│ ├── pageB.html # 入口文件b
│ ├── css/ # css资源
│ ├── img/ # 图片资源
│ ├── js # js&jsx资源
│ │ ├── pageA.js # a页面入口
│ │ ├── pageB.js # b页面入口
│ │ ├── lib/ # 没有存放在npm的第三方库或者下载存放到本地的基础库,如jQuery、Zepto、avalon
│ ├── pathmap.json # 手动配置某些模块的路径,可以加快webpack的编译速度
├── webpack.config.js # webpack配置入口
|
一:自动构建入口
官方多入口示例
webpack默认支持多入口,官方也有多入口的示例。配件文件webpack.config.js如下
1
2
3
4
5
6
7
8
9
10
11
12
13
|
//已简化
var
path = require(
"path"
);
module.exports = {
entry: {
pageA:
"./pageA"
,
pageB:
"./pageB"
},
output: {
path: path.join(__dirname,
"js"
),
filename:
"[name].bundle.js"
,
chunkFilename:
"[id].chunk.js"
}
}
|
每新增一个页面就需要在webpack.config.js的entry 中增加一个 pageC:"./pageC",页面少还好,页面一多,就有点麻烦了,而且配置文件,尽可能不改动。那么如何支持不修改配置呢?
自动构建入口函数
entry实际上是一个map对象,结构如下{filename:filepath},那么我们可以根据文件名匹配,很容易构造自动扫描器:
npm 中有一个用于文件名匹配的 glob模块,通过glob很容易遍历出src/js目录下的所有js文件:
安装glob模块
1
|
$ npm
install
glob --save-dev
|
修改webpack.config.js 配置,新增entries函数,修改entry:entries(),修改output的filename为"[name].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
|
//引入glob
var
glob = require(
'glob'
)
//entries函数
var
entries=
function
() {
var
jsDir = path.resolve(srcDir,
'js'
)
var
entryFiles = glob.sync(jsDir +
'/*.{js,jsx}'
)
var
map = {};
for
(
var
i = 0; i < entryFiles.length; i++) {
var
filePath = entryFiles[i];
var
filename = filePath.substring(filePath.lastIndexOf(
'\/'
) + 1, filePath.lastIndexOf(
'.'
));
map[filename] = filePath;
}
return
map;
}
//修改入口,已经修改outp的filename
module.exports = {
//entry: "./src/js/index.js",
entry: entries(),
output: {
path: path.join(__dirname,
"dist"
),
filename:
"[name].js"
},
......
//以下省略,可以见下文详细配置
|
测试
1.在src/js目录中新增pageA.js
1
2
3
|
//js只有两行代码,在body中加一句话
var
$ = require(
"jquery"
)
$(
"<div>这是jquery生成的多页面示例</div>"
).appendTo(
"body"
)
|
2.新增pageA.html,也顺便修改原来的index.html 对于js文件名的更改
1
2
3
4
5
6
7
8
9
10
|
<!DOCTYPE html>
<
html
lang
=
"en"
>
<
head
>
<
meta
charset
=
"UTF-8"
>
<
title
></
title
>
</
head
>
<
body
>
<
script
src
=
"../dist/index.js"
></
script
>
</
body
>
</
html
>
|
3.执行webpack,启动dev-server
1
2
|
$ webpack
$ webpack-dev-server
|
OK,成功打包生成pageA.js,成功运行
二:公共库单独打包
先来分析下,上个步骤打包的日志:
index.js 依赖了avalon 和 jquery,然后打包后的index.js 有480kb
pageA.js 只用了jquery,然后打包后的js 有294kb
那么如果引用的lib库多一点,又被很多页面引用,那么lib库就会被重复打包到page.js中去,模块越多重复加载的情况越严重。
如果把公共代码提取出来作为单独的js,那么就到处可以复用,浏览器也就可以进行缓存,这时候就需要用到webpack内置插件WebPack.optimize.CommonsChunkPlugin
CommonsChunkPlugin 介绍
使用
1
|
new
webpack.optimize.CommonsChunkPlugin(options)
|
Options
翻译得比较简单,详见官方说明:
- options.name or options.names(string|string[]): 公共模块的名称
- options.filename (string): 公开模块的文件名(生成的文件名)
- options.minChunks (number|Infinity|function(module,count) - boolean): 为number表示需要被多少个entries依赖才会被打包到公共代码库;为Infinity 仅仅创建公共组件块,不会把任何modules打包进去。并且提供function,以便于自定义逻辑。
- options.chunks(string[]):只对该chunks中的代码进行提取。
- options.children(boolean):如果为true,那么公共组件的所有子依赖都将被选择进来
- options.async(boolean|string):如果为true,将创建一个 option.name的子chunks(options.chunks的同级chunks) 异步common chunk
- options.minSize(number):所有公共module的size 要大于number,才会创建common chunk
2个常用的例子,更多例子见官方说明:
1.Commons chunk for entries:针对入口文件提取公共代码
1
2
3
4
5
6
7
8
9
10
11
12
13
|
new
CommonsChunkPlugin({
name:
"commons"
,
// (the commons chunk name)
filename:
"commons.js"
,
// (the filename of the commons chunk)
// minChunks: 3,
// (Modules must be shared between 3 entries)
// chunks: ["pageA", "pageB"],
// (Only use these entries)
})
|
2.Explicit vendor chunk:直接指定第三方依赖库,打包成公共组件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
entry: {
vendor: [
"jquery"
,
"other-lib"
],
app:
"./entry"
}
new
CommonsChunkPlugin({
name:
"vendor"
,
// filename: "vendor.js"
// (Give the chunk a different name)
minChunks: Infinity,
// (with more entries, this ensures that no other module
// goes into the vendor chunk)
})
|
CommonsChunkPlugin使用
基于上篇的项目,参考上面的第二个例子,我们将jquery 和 avalon 提取出来打包成vendor.js
完整的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
|
```js
var
webpack = require(
"webpack"
);
var
path = require(
"path"
);
var
srcDir = path.resolve(process.cwd(),
'src'
);
var
nodeModPath = path.resolve(__dirname,
'./node_modules'
);
var
pathMap = require(
'./src/pathmap.json'
);
var
glob = require(
'glob'
)
var
CommonsChunkPlugin = webpack.optimize.CommonsChunkPlugin;
var
entries=
function
() {
var
jsDir = path.resolve(srcDir,
'js'
)
var
entryFiles = glob.sync(jsDir +
'/*.{js,jsx}'
)
var
map = {};
for
(
var
i = 0; i < entryFiles.length; i++) {
var
filePath = entryFiles[i];
var
filename = filePath.substring(filePath.lastIndexOf(
'\/'
) + 1, filePath.lastIndexOf(
'.'
));
map[filename] = filePath;
}
return
map;
}
module.exports = {
//entry: "./src/js/index.js",
//entry: entries(),
entry: Object.assign(entries(), {
// 用到什么公共lib(例如jquery.js),就把它加进vendor去,目的是将公用库单独提取打包
'vendor'
: [
'jquery'
,
'avalon'
]
}),
output: {
path: path.join(__dirname,
"dist"
),
filename:
"[name].js"
},
module: {
loaders: [
{test: /\.css$/, loader:
'style-loader!css-loader'
}
]
},
resolve: {
extensions: [
'.js'
,
""
,
".css"
],
root: [srcDir,nodeModPath],
alias: pathMap,
publicPath:
'/'
},
plugins: [
new
CommonsChunkPlugin({
name:
'vendor'
,
minChunks: Infinity
})
]
}
```
|
测试、验证
1.修改入口(Object.assign 是html5.js里面的....)
1
2
3
4
5
|
//entry: entries(),
entry: Object.assign(entries(), {
// 用到什么公共lib(例如jquery.js),就把它加进vendor去,目的是将公用库单独提取打包
'vendor'
: [
'jquery'
,
'avalon'
]
}),
|
2.加入插件CommonsChunkPlugin
1
2
3
4
5
6
7
8
|
var
CommonsChunkPlugin = webpack.optimize.CommonsChunkPlugin;
config 中增加 plugins
plugins: [
new
CommonsChunkPlugin({
name:
'vendor'
,
minChunks: Infinity
})
]
|
3.修改index.html 和 pageA.html,增加对verdor.js的引用
1
2
3
|
<
script
src
=
"../dist/vendor.js"
></
script
>
<
script
src
=
"../dist/index.js"
></
script
>
//<
script
src
=
"../dist/pageA.js"
></
script
>
|
4.执行webpack
$ webpack
结果分析
可以看到index.js 就只有457 bytes了,pageA.js 227bytes。vendor.js 是集成了jquery+avalon,所以有488kb。
这样vendor.js 就可以重复利用了,也方便浏览器进行缓存。
调试过程中发现
Uncaught ReferenceError: webpackJsonp is not defined
这个是因为当时把vendor.js引入 放到了page.js 后面,导致page.js执行异常,所以,请一定把vendor.js 放在前面。
生成后的index.js就很轻便了,第三方库都被打包到vendor中了,代码如下:
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
|
webpackJsonp([0],[
/* 0 */
/***/
function
(module, exports, __webpack_require__) {
/**
* Created by sloong on 2016/6/1.
*/
//avalon 测试
var
avalon = __webpack_require__(1);
avalon.define({
$id:
"avalonCtrl"
,
name:
"Hello Avalon!"
});
/*
//zepto 测试
require("zepto")
$("<div>这是zepto生成的</div>").appendTo("body")*/
//jquery 测试
var
$ = __webpack_require__(2)
$(
"<div>这是jquery生成的</div>"
).appendTo(
"body"
)
/***/
}
]);
|
页面测试均正常
OK,本篇结束了。如何让webpack 自动在html文件中引入所需js的script标签,如何给js和css文件加了hash值,这样浏览器每次都能检测到文件变更,而且也不需要手动修改引入的js文件链接,这些操作webpack都能轻松搞定。