详解webpack 多页面/入口支持&公共组件单独打包


Posted in Javascript onJune 29, 2017

webpack系列目录

基于webpack搭建纯静态页面型前端工程解决方案模板, 最终形态源码见github: https://github.com/ifengkou/webpack-template

正文

本篇主要介绍:如何自动构建入口文件,并生成对应的output;公共js库如何单独打包。

多入口文件,自动扫描入口。同时支持SPA和多页面型的项目

公共js库如何单独打包。

上一篇示例,主要介绍如何集成第三方js库到项目中使用,如jquery。示例的入口只有一个index,而且是将公共js库连同page.js一起打包到output.js中。那么在开发中会出现,每新增一个页面模块,就需要修改webpack.config.js配置文件(增加一个入口),而且如果用到的第三方库比较多,这样也容易导致jquery,React等代码库重复被合并到打包后的js,导致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默认支持多入口,官方也有多入口的示例。配件文件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"

//引入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

//js只有两行代码,在body中加一句话
var $ = require("jquery")
$("<div>这是jquery生成的多页面示例</div>").appendTo("body")

2.新增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>

3.执行webpack,启动dev-server

$ webpack
$ webpack-dev-server

详解webpack 多页面/入口支持&amp;公共组件单独打包

OK,成功打包生成pageA.js,成功运行

二:公共库单独打包

先来分析下,上个步骤打包的日志:

详解webpack 多页面/入口支持&amp;公共组件单独打包

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
翻译得比较简单,详见官方说明:

  1. options.name or options.names(string|string[]): 公共模块的名称
  2. options.filename (string): 公开模块的文件名(生成的文件名)
  3. options.minChunks (number|Infinity|function(module,count) - boolean): 为number表示需要被多少个entries依赖才会被打包到公共代码库;为Infinity 仅仅创建公共组件块,不会把任何modules打包进去。并且提供function,以便于自定义逻辑。
  4. options.chunks(string[]):只对该chunks中的代码进行提取。
  5. options.children(boolean):如果为true,那么公共组件的所有子依赖都将被选择进来
  6. options.async(boolean|string):如果为true,将创建一个 option.name的子chunks(options.chunks的同级chunks) 异步common chunk
  7. options.minSize(number):所有公共module的size 要大于number,才会创建common chunk

2个常用的例子,更多例子见官方说明:

1.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)
})

2.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)
})

CommonsChunkPlugin使用

基于上篇的项目,参考上面的第二个例子,我们将jquery 和 avalon 提取出来打包成vendor.js

完整的webpack.config.js 如下:

```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里面的....)

//entry: entries(),
entry: Object.assign(entries(), {
 // 用到什么公共lib(例如jquery.js),就把它加进vendor去,目的是将公用库单独提取打包
 'vendor': ['jquery', 'avalon']
}),

2.加入插件CommonsChunkPlugin

var CommonsChunkPlugin = webpack.optimize.CommonsChunkPlugin;
config 中增加 plugins
 plugins: [
  new CommonsChunkPlugin({
   name: 'vendor',
   minChunks: Infinity
  })
 ]

3.修改index.html 和 pageA.html,增加对verdor.js的引用

<script src="../dist/vendor.js"></script>
<script src="../dist/index.js"></script>
//<script src="../dist/pageA.js"></script>

4.执行webpack

$ webpack

详解webpack 多页面/入口支持&amp;公共组件单独打包

结果分析

可以看到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中了,代码如下:

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")

/***/ }
]);

页面测试均正常

详解webpack 多页面/入口支持&amp;公共组件单独打包

OK,本篇结束了。如何让webpack 自动在html文件中引入所需js的script标签,如何给js和css文件加了hash值,这样浏览器每次都能检测到文件变更,而且也不需要手动修改引入的js文件链接,这些操作webpack都能轻松搞定

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
关于__defineGetter__ 和__defineSetter__的说明
May 12 Javascript
js弹出层之1:JQuery.Boxy (二)
Oct 06 Javascript
JavaScript中的alert()函数使用技巧详解
Dec 29 Javascript
JS给超链接加确认对话框的方法
Feb 24 Javascript
Javascript中With语句用法实例
May 14 Javascript
JavaScript基本的输出和嵌入式写法教程
Oct 20 Javascript
jQuery插件实现静态HTML验证码校验
Nov 06 Javascript
Jquery技巧(必须掌握)
Mar 16 Javascript
jQuery EasyUi 验证功能实例解析
Jan 06 Javascript
jQuery实现获取隐藏div高度的方法示例
Feb 09 Javascript
webstorm和.vue中es6语法报错的解决方法
May 08 Javascript
微信小程序用canvas画图并分享
Mar 09 Javascript
详解webpack 如何集成第三方js库
Jun 29 #Javascript
详解webpack介绍&amp;安装&amp;常用命令
Jun 29 #Javascript
基于node.js制作简单爬虫教程
Jun 29 #Javascript
JavaScript算法教程之sku(库存量单位)详解
Jun 29 #Javascript
详解webpack自动生成html页面
Jun 29 #Javascript
JS判断Android、iOS或浏览器的多种方法(四种方法)
Jun 29 #Javascript
详解webpack分包及异步加载套路
Jun 29 #Javascript
You might like
《DOTA3》开发工作已经开始 《DOTA3》将代替《DOTA2》
2021/03/06 DOTA
sql注入与转义的php函数代码
2013/06/17 PHP
php轻松实现中英文混排字符串截取
2014/05/28 PHP
PHP获取当前日期和时间及格式化方法参数
2015/05/11 PHP
Joomla框架实现字符串截取的方法示例
2017/07/18 PHP
浅析Js(Jquery)中,字符串与JSON格式互相转换的示例(直接运行实例)
2013/07/09 Javascript
jQuery Validate 验证,校验规则写在控件中的具体实例
2014/02/27 Javascript
Jquery 分页插件之Jquery Pagination
2015/08/25 Javascript
javascript中sort() 方法使用详解
2015/08/30 Javascript
javascript对浅拷贝和深拷贝的详解
2016/10/14 Javascript
jQuery中的siblings()是什么意思(推荐)
2016/12/29 Javascript
JS操作input标签属性checkbox全选的实现代码
2017/03/02 Javascript
基于Vue制作组织架构树组件
2017/12/06 Javascript
微信小程序框架的页面布局代码
2019/08/17 Javascript
JS模拟浏览器实现全局搜索功能
2019/09/11 Javascript
node 解析图片二维码的内容代码实例
2019/09/11 Javascript
vue微信分享插件使用方法详解
2020/02/18 Javascript
JS内置对象和Math对象知识点详解
2020/04/03 Javascript
Python中的jquery PyQuery库使用小结
2014/05/13 Python
Python中使用SAX解析xml实例
2014/11/21 Python
在Python中定义和使用抽象类的方法
2016/06/30 Python
Ubuntu下使用python读取doc和docx文档的内容方法
2018/05/08 Python
django实现支付宝支付实例讲解
2019/10/17 Python
python调用函数、类和文件操作简单实例总结
2019/11/29 Python
jupyter notebook oepncv 显示一张图像的实现
2020/04/24 Python
python IDLE添加行号显示教程
2020/04/25 Python
Pyecharts 中Geo函数常用参数的用法说明
2021/02/01 Python
智能电子秤、手表和健康监测仪:Withings(之前为诺基亚健康)
2018/10/30 全球购物
TripAdvisor日本:全球领先的旅游网站
2019/02/14 全球购物
struct和class的区别
2015/11/20 面试题
求职信的最佳写作思路
2014/02/01 职场文书
《二泉映月》教学反思
2014/04/15 职场文书
经贸日语专业自荐信
2014/09/02 职场文书
党的群众路线教育实践活动个人对照检查材料(企业)
2014/11/05 职场文书
2014年为民办实事工作总结
2014/12/20 职场文书
浅谈Redis在直播场景的实践方案
2021/04/27 Redis