webpack2--4多页面支持& 公共组件单独打包
- 本篇主要介绍:如何自动构建入口文件,并生成对应的output;公共js库如何单独打包。
- 多入口文件,自动扫描入口。同时支持SPA和多页面型的项目.
- 公共js库如何单独打包。
基础目录
以下示例基于上一篇进行改进,上一篇项目源码
.
├── 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.config.js如下
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模块npm install glob --save-dev
修改webpack.config.js 配置,新增entries函数,修改entry:entries(),修改output的filename为"[name].js"
var glob = require('glob')
//entries函数
var entries = function() {
var jsDir = path.resolve(srcDir, 'js')
//执行同步全局搜索,返回{array<string>}文件名数组对象
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"
},
......
//以下省略,可以见下文详细配置
测试
- 在src/js目录中新增pageA.js
//js只有两行代码,在body中加一句话
var $ = require("jquery")
$("<div>这是jquery生成的多页面示例</div>").appendTo("body")
- 新增pageA.html,也顺便修改index.html对于js文件名的更改
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<script src="../dist/index.js"></script>
</body>
</html>
- 执行webpack,启动webpack-dev-server
$ webpack
$ webpack-dev-server
公共库单独打包
之前index.js 依赖了avalon 和 jquery,然后打包后的index.js 有480kb
pageA.js 只用了jquery,然后打包后的js 有294kb
那么如果引用的lib库多一点,又被很多页面引用,那么lib库就会被重复打包到page.js中去,模块越多重复加载的情况越严重。
如果把公共代码提取出来作为单独的js,那么就到处可以复用,浏览器也就可以进行缓存,这时候就需要用到webpack
内置插件WebPack.optimize.CommonsChunkPlugin
CommonsChunkPlugin介绍
new webpack.optimize.CommonsChunkPlugin(options)
options
{
name:string,//公共模块的名称
names:string|string[],//公共模块名称
minChunks :number|Infinity|function(module,count) - boolean,//为number表示需要被多少个entries依赖才会被打包到公共代码库;为Infinity 仅仅创建公共组件块,不会把任何modules打包进去。并且提供function,以便于自定义逻辑。
chunks:string[],//只对该chunks中的代码进行提取
children:boolean,//为true时候,那么公共组件的所有子依赖都将被选择进来
async:boolean|string,//如果为true,将创建一个 option.name的子chunks(options.chunks的同级chunks) 异步common chunk
minSize:number//所有公共module的size 要大于number,才会创建common chunk
}
列子
- Commons chunk for entries:针对入口文件提取公共代码
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)
})
- Explicit vendor chunk:直接指定第三方依赖库,打包成公共组件
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)
})
修改webpack.config.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
})
]
}
修改index.html和pageA.html,增加对vendor.js的引用
<script src="../dist/vendor.js"></script>
<script src="../dist/index.js"></script>
<script src="../dist/pageA.js"></script>
执行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 放在前面。