详解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 单引号和双引号的区别及使用注意
Jul 31 Javascript
jquery购物车实时结算特效实现思路
Sep 23 Javascript
js键盘上下左右键怎么触发function(实例讲解)
Dec 14 Javascript
javascript中取前n天日期的两种方法分享
Jan 26 Javascript
javascript获取flash版本号的方法
Nov 20 Javascript
JS DOM实现鼠标滑动图片效果
Sep 17 Javascript
原生JS轮播图插件
Feb 09 Javascript
JS的函数调用栈stack size的计算方法
Jun 24 Javascript
JS 中可以提升幸福度的小技巧(可以识别更多另类写法)
Jul 28 Javascript
vue 实现左右拖拽元素并且不超过他的父元素的宽度
Nov 30 Javascript
layui 上传文件_批量导入数据UI的方法
Sep 23 Javascript
JS中的const命令你真懂它吗
Mar 08 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操作mysql数据库分表的方法
2016/06/09 PHP
用HTML/JS/PHP方式实现页面延时跳转的简单实例
2016/07/18 PHP
php+jQuery实现的三级导航栏下拉菜单显示效果
2017/08/10 PHP
var与Javascript变量隐式声明
2009/09/17 Javascript
DOM Scripting中的图片切换[兼容Firefox]
2010/06/12 Javascript
用JavaScript对JSON进行模式匹配 (Part 2 - 实现)
2010/07/17 Javascript
在JavaScript并非所有的一切都是对象
2013/04/11 Javascript
jquery自动将form表单封装成json的具体实现
2014/03/17 Javascript
深入浅析JavaScript字符串操作方法 slice、substr、substring及其IE兼容性
2015/12/16 Javascript
详解AngularJS中的表单验证(推荐)
2016/11/17 Javascript
微信小程序 常用工具类详解及实例
2017/02/15 Javascript
jquery实现倒计时小应用
2017/09/19 jQuery
vue项目动态设置页面title及是否缓存页面的问题
2018/11/08 Javascript
简单使用webpack打包文件的实现
2019/10/29 Javascript
Python中title()方法的使用简介
2015/05/20 Python
python实现比较两段文本不同之处的方法
2015/05/30 Python
python实现class对象转换成json/字典的方法
2016/03/11 Python
Python爬虫辅助利器PyQuery模块的安装使用攻略
2016/04/24 Python
浅谈DataFrame和SparkSql取值误区
2018/06/09 Python
解决Python中定时任务线程无法自动退出的问题
2019/02/18 Python
Django admin禁用编辑链接和添加删除操作详解
2019/11/15 Python
基于Python中的yield表达式介绍
2019/11/19 Python
记一次django内存异常排查及解决方法
2020/08/07 Python
发现世界上最好的珠宝设计师:JewelStreet
2017/12/17 全球购物
STRATHBERRY苏贝瑞包包官网:西班牙高级工匠手工打造
2020/11/10 全球购物
大学本科毕业生求职简历的自我评价
2013/10/09 职场文书
护士实习自我鉴定
2013/10/22 职场文书
人事部经理岗位职责
2014/03/07 职场文书
成本会计实训报告
2014/11/05 职场文书
2014年医药代表工作总结
2014/11/22 职场文书
寒暑假实习证明书模板
2014/11/29 职场文书
教师岗位职责
2015/02/03 职场文书
项目战略合作意向书
2015/05/08 职场文书
python基于tkinter制作m3u8视频下载工具
2021/04/24 Python
opencv-python图像配准(匹配和叠加)的实现
2021/06/23 Python
Win11绿屏怎么办?Win11绿屏死机的解决方法
2021/11/21 数码科技