详解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 相关文章推荐
JQuery 常用方法基础教程
Feb 06 Javascript
js表数据排序 sort table data
Feb 18 Javascript
ASP.NET jQuery 实例15 通过控件CustomValidator验证CheckBoxList
Feb 03 Javascript
JS对HTML标签select的获取、添加、删除操作
Oct 17 Javascript
textarea焦点的用法实现获取焦点清空失去焦点提示效果
May 19 Javascript
js生成的验证码的实现与技术分析
Sep 17 Javascript
详解JavaScript基于面向对象之创建对象(2)
Dec 10 Javascript
vuejs通过filterBy、orderBy实现搜索筛选、降序排序数据
Oct 26 Javascript
Angular2下使用pdf插件的方法详解
Apr 29 Javascript
详解EasyUi控件中的Datagrid
Aug 23 Javascript
微信小程序 scroll-view 水平滚动实现过程解析
Oct 12 Javascript
js实现倒计时秒杀效果
Mar 25 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
你可能不知道PHP get_meta_tags()函数
2014/05/12 PHP
php防止站外远程提交表单的方法
2014/10/20 PHP
php验证session无效的解决方法
2014/11/04 PHP
PHP中使用php://input处理相同name值的表单数据
2015/02/03 PHP
php实现俄罗斯乘法实例
2015/03/07 PHP
php使用指定编码导出mysql数据到csv文件的方法
2015/03/31 PHP
Laravel5.5 视图 - 创建视图和数据传递示例
2019/10/21 PHP
PHP7 list() 函数修改
2021/03/09 PHP
Document 对象的常用方法
2009/07/31 Javascript
IE6弹出“已终止操作”的解决办法
2010/11/27 Javascript
JavaScript两种跨域技术全面介绍
2014/04/16 Javascript
JavaScript中操作字符串小结
2015/05/04 Javascript
jQuery+canvas实现简单的球体斜抛及颜色动态变换效果
2016/01/28 Javascript
浅谈javascript中new操作符的原理
2016/06/07 Javascript
jquery 动态增加,减少input表单的简单方法(必看)
2016/10/12 Javascript
JavaScript给每一个li节点绑定点击事件的实现方法
2016/12/01 Javascript
微信小程序 input输入框控件详解及实例(多种示例)
2016/12/14 Javascript
详解ionic本地相册、拍照、裁剪、上传(单图完全版)
2017/10/10 Javascript
Vue中使用webpack别名的方法实例详解
2018/06/19 Javascript
JQuery模拟实现网页中自定义鼠标右键菜单功能
2018/11/14 jQuery
Vue.js组件使用props传递数据的方法
2019/10/19 Javascript
mustache.js实现首页元件动态渲染的示例代码
2020/12/28 Javascript
python实现读取命令行参数的方法
2015/05/22 Python
Flask框架URL管理操作示例【基于@app.route】
2018/07/23 Python
使用python将mysql数据库的数据转换为json数据的方法
2019/07/01 Python
Pandas读取csv时如何设置列名
2020/06/02 Python
python3.9和pycharm的安装教程并创建简单项目的步骤
2021/02/03 Python
纯CSS3实现的8种Loading动画效果
2014/07/05 HTML / CSS
适合各种场合的美食礼品:Harry & David
2016/08/03 全球购物
韩国最大的购物网站:Gmarket
2019/06/20 全球购物
双立人美国官方商店:ZWILLING集团餐具和炊具
2020/05/07 全球购物
质检部部长职责
2013/12/16 职场文书
环保倡议书500字
2014/05/15 职场文书
拒绝黄毒毒宣传标语
2014/06/26 职场文书
岗位安全生产责任书
2014/07/28 职场文书
初中思想品德教学反思
2016/02/24 职场文书