解读webpack的bundle.js

      可能就是好奇心略重了,读了一下webpack打包后的bundle.js的代码,复杂的模块可能读不懂,但简单的hello world模块我仍是能看懂的。没什么目的,就是想经过几个简单的模块,一条简单的webpack命令,一个神奇的bundle.js代码来了解webpack是怎么把遵循commonJs规范的模块应用到浏览器端的。webpack

几个简单的模块:
web

一条简单的webpack命令:
数组

一个神奇的bundle.js:浏览器

 1 /******/ (function(modules) { // webpackBootstrap
 2 /******/     // The module cache
 3 /******/     var installedModules = {};
 4 
 5 /******/     // The require function
 6 /******/     function __webpack_require__(moduleId) {
 7 
 8 /******/         // Check if module is in cache
 9 /******/         if(installedModules[moduleId])
10 /******/             return installedModules[moduleId].exports;
11 
12 /******/         // Create a new module (and put it into the cache)
13 /******/         var module = installedModules[moduleId] = {
14 /******/             exports: {},
15 /******/             id: moduleId,
16 /******/             loaded: false
17 /******/         };
18 
19 /******/         // Execute the module function
20 /******/         modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
21 
22 /******/         // Flag the module as loaded
23 /******/         module.loaded = true;
24 
25 /******/         // Return the exports of the module
26 /******/         return module.exports;
27 /******/     }
28 
29 
30 /******/     // expose the modules object (__webpack_modules__)
31 /******/     __webpack_require__.m = modules;
32 
33 /******/     // expose the module cache
34 /******/     __webpack_require__.c = installedModules;
35 
36 /******/     // __webpack_public_path__
37 /******/     __webpack_require__.p = "";
38 
39 /******/     // Load entry module and return exports
40 /******/     return __webpack_require__(0);
41 /******/ })
42 /************************************************************************/
43 /******/ ([
44 /* 0 */
45 /***/ function(module, exports, __webpack_require__) {
46 
47     /*
48       打印文本的index模块
49      */
50     var text = __webpack_require__(1);
51     console.log(text);
52 
53 /***/ },
54 /* 1 */
55 /***/ function(module, exports) {
56 
57     /*
58       生成文本的Hello world模块
59      */
60     module.exports = 'Hello world!';
61 
62 /***/ }
63 /******/ ]);
View Code

注释太多有点懵逼,但是细看也不就是一个当即执行的函数表达式嘛(IIFE),这是JavaScript中常见的独立做用域的方法。这里我将注释简化一下:缓存

 1 (function(modules){
 2     //module缓存对象
 3     var installedModules = {};
 4     //require函数
 5     function __webpack_require__(moduleId){
 6         //检查module是否在cache中
 7         if(installedModules[moduleId]){
 8             return installedModules[moduleId].exports;
 9         }
10         //若不在cache中则新建module并放入cache中
11         var module = installedModules[moduleId] = {
12             exports: {},
13             id: moduleId,
14             loaded: false
15         };
16         //执行module函数
17         modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
18         //标记module已经加载
19         module.loaded = true;
20         //返回module的导出模块
21         return module.exports;
22     }
23 
24     //暴露modules对象(__webpack_modules__)
25     __webpack_require__.m = modules;
26     //暴露modules缓存
27     __webpack_require__.c = installedModules;
28     //设置webpack公共路径__webpack_public_path__
29     __webpack_require__.p = "";
30     //读取入口模块而且返回exports导出
31     return __webpack_require__(0);
32     
33 })([function(module, exports, __webpack_require__){ /*模块Id为0*/
34     var text = __webpack_require__(1);
35     console.log(text);
36 },function(module, exports){ /*模块Id为1*/
37     module.exports = 'Hello world';
38 }]);

这个IIFE接收一个数组做为参数modules,数组的每一项都是一个匿名函数表明一个模块(index.js和hello.js模块)。但是为何构建命令中只出现了“index.js"呢,这是由于webpack经过静态分析index.js文件获得语法树,递归检测index.js所依赖的模块,以及依赖的依赖,入口模块和全部的依赖模块做为数组中的项,并合并到最终的代码中。ide

正是因为模块代码被包装成函数后,每一个模块的运行时机变的可控,能够决定什么时候调用(经过 modules[moduleId].call ),并且也有了独立的做用域,定义变量,声明函数都不会污染全局做用域。模块函数的参数列表中除了commonJS规范所要求的 module 与 exports 外,还有 __webpack_require__ 函数,用来替换 require ,做用是声明对其余模块的依赖并得到该模块的 exports ( return installedModules[moduleId].exports 和 return module.exports; )。并且不须要提供模块的相对路径或其余形式的ID,直接传入该模块在modules列表中索引便可,这样能够省掉模块标识符的解析过程(准确说,webpack是把require模块的解析过程提早到了构建期),从而能够得到更好的运行性能。

函数

程序解读:
bundle.js经过 __webpack_require__(0); 启动整个程序,先检查模块ID = 0是否在缓存对象中,若该模块的缓存存在返回 module.exports 即模块所暴露出来的数据,若该模块的缓存不在则新建立module对象(该module对象做用是用来指向真实模块)并加入到缓存对象中,此时因为module对象和该模块的缓存对象 installedModules[moduleId] 的exports属性为没有数据,因此须要经过执行该模块函数来返回具体require其余模块的数据,传入的上下文对象是 module.exports 和 installedModules[moduleId].exports 所共同指向的一个对象。当程序执行到 var text = __webpack_require__(1); 时,又会执行 modules[1].call ,而后 module.exports = 'Hello world'; 将执行 __webpack_require__(1) 时建立的module1的exports赋值为Hello world,并返回,此时 __webpack_require__(1) 执行完毕,text为Hello world并打印, __webpack_require__(0) 执行完毕。这是一个递归的过程,若是还有更多依赖模块的话会更明显。

总结一下,webpack主要作了两部分工做:
1.分析获得全部必须模块并合并。
2.提供让这些模块有序,正常的执行环境。

性能

参考:
《React全栈 Redux+Flux+webpack+Babel整合开发》ui

相关文章
相关标签/搜索