webpack的bundle文件分析

一、一个入口,一个文件

  • webpack.config.js
module.exports = {
entry: './main.js',  // 一个入口
output: {
  filename: 'bundle.js'
}
};
  • main.js
document.write('<h1>Hello World</h1>');  // 一个文件
  • bundle.js
/******/ (function(modules) { // webpackBootstrap
/******/     // module缓存对象
/******/     var installedModules = {};
/******/
/******/     // require函数
/******/     function __webpack_require__(moduleId) {
/******/
/******/         // 检查module是否在缓存当中,若在,则返回exports对象
/******/         if(installedModules[moduleId]) {
/******/             return installedModules[moduleId].exports;
/******/         }
/******/         // 若不在,则以moduleId为key创建一个module,并放入缓存当中
/******/         var module = installedModules[moduleId] = {
/******/             i: moduleId,
/******/             l: false,
/******/             exports: {}
/******/         };
/******/
/******/         // 执行module函数
/******/         modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/         // 标志module已经加载
/******/         module.l = true;
/******/
/******/         // 返回module的导出模块
/******/         return module.exports;
/******/     }
/******/
/******/
/******/     // 暴露modules对象(__webpack_modules__)
/******/     __webpack_require__.m = modules;
/******/
/******/     // 暴露module缓存
/******/     __webpack_require__.c = installedModules;
/******/
/******/     // 认证和谐导入模块具有正确的上下文的函数
/******/     __webpack_require__.i = function(value) { return value; };
/******/
/******/     // 为和谐导入模块定义getter函数
/******/     __webpack_require__.d = function(exports, name, getter) {
/******/         if(!__webpack_require__.o(exports, name)) {
/******/             Object.defineProperty(exports, name, {
/******/                 configurable: false,
/******/                 enumerable: true,
/******/                 get: getter
/******/             });
/******/         }
/******/     };
/******/
/******/     // 兼容非和谐模块的getDefaultExport函数
/******/     __webpack_require__.n = function(module) {
/******/         var getter = module && module.__esModule ?
/******/             function getDefault() { return module['default']; } :
/******/             function getModuleExports() { return module; };
/******/         __webpack_require__.d(getter, 'a', getter);
/******/         return getter;
/******/     };
/******/
/******/     // Object.prototype.hasOwnProperty.call
/******/     __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/     // 设置webpack公共路径__webpack_public_path__
/******/     __webpack_require__.p = "";
/******/
/******/     // 读取入口模块,返回exports导出
/******/     return __webpack_require__(__webpack_require__.s = 0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports) {  // 模块ID为0
document.write('<h1>Hello World</h1>');
/***/ })
/******/ ]);
  • 整体分析
    整个的bundle.js是一个立即执行函数表达式(IIFE),传入的参数modules是一个数组,数组的每一项都是一个匿名函数,代表一个模块。在这里,数组的第一个参数是一个function,里面的内容就是原先main.js里面的内容。
    IIFE里面存在一个闭包,webpack_require_是模块加载函数,作用是声明对其他模块的依赖,并返回exports。参数为模块的Id(每一个模块都有一个唯一的id),不需要提供模块的相对路径,可以省掉模块标识符的解析过程(准确说,webpack是把require模块的解析过程提前到了构建期),从而可以获得更好的运行性能。
    执行modules函数的是:
modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);

通过借用call来使函数的this始终为module本身,参数webpack_require是为了让modules有加载其他模块的能力。

  • 程序流程
    bundle通过webpack_require(webpack_require.s = 0)来启动整个程序。首先看看缓存中有没有ID为0的模块,若存在则返回缓存中的exports暴露出来的对象;若不存在,则新建module对象,并放入缓存当中。此时,module对象和该模块的缓存对象installedModules[moduleId]还没有数据,所以要执行该模块来返回具体require其他模块的数据。传入的context是module.exports(等于installedModules[moduleId].exports)。

二、一个入口,多个文件

main1包含inner1;main2包含inner1和inner2。

  • webpack.config.js
module.exports = {
entry: {
  bundle1: './main1.js',
  bundle2: './main2.js'
},
output: {
  filename: '[name].js'
}
};
  • main1.js
var inner1 = require('./inner1.js');
inner1.inner1();
document.write("<h1>我是main1</h1>")
  • main2.js
var inner1 = require('./inner1.js');
var inner2 = require('./inner2.js');
inner1.inner1();
inner2.inner2();
document.write("<h1>我是main2</h1>");
  • bundle1.js
/******/ (function(modules) { // webpackBootstrap
/******/ 这部分和上面的一样
/******/     // Load entry module and return exports
/******/     return __webpack_require__(__webpack_require__.s = 2);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports) {
exports.inner1 = function() {
  document.write('<h1>我是inner1</h1>');
}
/***/ }),
/* 1 */,
/* 2 */
/***/ (function(module, exports, __webpack_require__) {
var inner1 = __webpack_require__(0);
inner1.inner1();
document.write("<h1>我是main1</h1>")
/***/ })
/******/ ]);
  • bundle2.js
/******/ (function(modules) { // webpackBootstrap
/******/ 这部分和上面的一样
/******/     // Load entry module and return exports
/******/     return __webpack_require__(__webpack_require__.s = 3);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports) {
exports.inner1 = function() {
  document.write('<h1>我是inner1</h1>');
}
/***/ }),
/* 1 */
/***/ (function(module, exports) {
exports.inner2 = function() {
  document.write('<h1>我是inner2</h1>');
}
/***/ }),
/* 2 */,
/* 3 */
/***/ (function(module, exports, __webpack_require__) {
var inner1 = __webpack_require__(0);
var inner2 = __webpack_require__(1);
inner1.inner1();
inner2.inner2();
document.write("<h1>我是main2</h1>");
/***/ })
/******/ ]);
index.html
  • 整体分析
    对于多入口文件的情况,分别独立执行单个入口的情况,每个入口文件互不干扰。
    从上面可以看到,两个入口文件main1.js和main2.js的module id都是0,所以可以知道每个入口文件对应的module id都是0。又因为每个module id都是全局唯一的,所以在main1中没有1;在main2中没有2。
    静态分析打包是事先生成chunk,inner1.js文件被重复包含了,如果需要消除模块冗余,可以通过CommonsChunkPlugin插件来对公共依赖模块进行提取。
CommonsChunkPlugin初始化参数 含义
name 给这个包含公共代码的chunk命名(唯一标识)。
filename 如何命名打包后生产的js文件。
minChunks 公共代码的判断标准:某个js模块被多少个chunk加载了才算是公共代码。
chunks 表示需要在哪些chunk(也可以理解为webpack配置中entry的每一项)里寻找公共代码进行打包。不设置此参数则默认提取范围为所有的chunk。

四、总结

  • bundle.js文件是一个立即执行函数表达式,传入参数是一个数组modules。真正执行module的是modules[moduleId].call(module.exports, module, module.exports, webpack_require);。
  • modules数组用来保存模块初始化函数,里面存储着真正用到的模块内容以及一些id信息。
  • installedModules对象用来保存module缓存对象,方便其他模块使用。
  • webpack_require模块加载函数,require时得到的对象,参数为模块id。
  • installedModules[moduleId].exports === module.exports === webpack_exports
    总的来说,webpack分析得到所有必须模块并合并;提供让这些模块有序执行的环境。
    代码链接https://github.com/cttin/demo/tree/master/webpack
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容