注:本文重点不是怎样配置webpack.config.js
并实现相应的功能,而是经过对比webpack编译前和编译后文件,探究webpack打包后的文件是怎样加载执行的。
本文讨论commonJS
模块化方案时,webpack
的打包工做。
为了便于讨论,咱们准备了一个很是简单的例子,涉及三个文件,分别是javascript
文件a.js
html
module.exports ={ say:function(){ console.log('A is saying.'); } }
文件b.js
java
module.exports ={ say:function(){ console.log('B is saying.'); } }
文件index.js
webpack
var a = require('./a'); var b = require('./b'); a.say(); b.say();
依赖关系很是简单,即index.js
文件依赖a.js
和b.js
两个文件。并且是采用commonJS方式来引用的。
个人config
文件也贴一下。git
var htmlPlugin = require('html-webpack-plugin'); module.exports = { entry:{ index:'./src/index.js' }, output:{ path:'builds', filename:'[name].js', chunkFilename:'chunk.[name].js' }, plugins:[ new htmlPlugin({ filename:__dirname+'/builds/index.html', template:'./index.html' }) ], devServer:{ contentBase:'./builds', inline:true } }
还有一个很是简单的index.html
文件。github
<!DOCTYPE html> <html> <head> <title>commonJS测试</title> </head> <body> </body> </html>
另外,本问的示例代码已经放到Github
上,请 点击这里查看。
咱们在目录下运行一下webpack
命令,builds文件夹里的文件,就是被webpack处理过的文件,也是咱们要讨论的重点。
咱们来看看文件目录:web
builds
里,是编译后的文件,src里,是咱们的原始文件。
那么a.js
和b.js
呢?打包进builds/index.js
文件里面了。打包其实就是干这个的,把多个文件合并到一个或少数几个文件里。
咱们点开builds/index.js
,发现咱们的代码被改的面目全非。你们点击这里看完整的代码,下面是部分片断。express
(function(modules){ var installedModules = {}; function __webpack_require__(moduleId) {/*省略*/} /*省略*/ return __webpack_require__(0); })([ //这部分是index.js function(module, exports, __webpack_require__){ var a = __webpack_require__(1); var b = __webpack_require__(2); a.say(); b.say(); }, //这部分是a.js function(module, exports){ module.exports ={ say:function(){ console.log('A is saying.'); } } }, //这部分是b.js function(module, exports){ module.exports ={ say:function(){ console.log('B is saying.'); } } } ])
在上面代码中,咱们的index.js
、a.js
和b.js
被放进了一个数组中。入口文件(index.js
)都是在数组的0
的位置。入口文件的依赖(a.js
和b.js
),会根据依赖的状况日后排。
这个数组做为IIFE中函数的参数modules
传入function
中。注意匿名函数中的return __webpack_require__(0);
,这句调用将执行数组中的第一个函数。数组
第一个函数:webpack-dev-server
function(module, exports, __webpack_require__) { var a = __webpack_require__(1); var b = __webpack_require__(2); a.say(); b.say(); }
__webpack_require__(1)也就是执行数组中的第二个函数。
第二个函数:
function(module, exports) { module.exports ={ say:function(){ console.log('A is saying.'); } } }
第二个函数的执行后的结果,就是把module.exports
中的内容赋给了变量a
。到这里,你们对代码的结构有了了解,可是关键的__webpack_require__
方法是怎样工做的呢?咱们来分析下代码:
function __webpack_require__(moduleId) { // Check if module is in cache if(installedModules[moduleId]) return installedModules[moduleId].exports; // Create a new module (and put it into the cache) var module = installedModules[moduleId] = { exports: {}, id: moduleId, loaded: false }; // Execute the module function modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); // Flag the module as loaded module.loaded = true; // Return the exports of the module return module.exports; }
这里注意modules[moduleId].call(module.exports, module, module.exports, __webpack_require__)
这行代码。其中的modules
就是咱们刚才讲的数组(modules
不是module
)。module.exports
是模块的上下文,也就是this
。因此,若是咱们在index.js
里加一句this.name='jack'
,那么最终这个等价于module.exports.name='jack'
;
此外,咱们还能够访问到module.id
这个属性。这并无什么实际的做用,只是能够加深咱们的理解。好比,我在a.js
中:
module.exports ={ say:function(){ console.log('A is saying.'); console.log(module.id); } }
是能够输出该模块的id
的。多说一句,若是你用webpack-dev-server
,它会打包进去其余的文件,这个id
会变的比较大(我测试的是75)。有一个可能会用到的module.loaded
属性。咱们的模块在第一次执行的时候,module.loaded
仍是false
,执行事后才被设置为true
。
好比下面的代码:
index.js
if (module.loaded) { var a = require('./a'); a.say(); } else { var b = require('./b'); b.say(); }
在咱们的例子中,这个是每次执行的结果都是B is saying.
由于每次执行index.js
模块中的代码都是第一次执行。
在实际的开发中,有可能有要判断当前代码是否是第一次执行的需求。
还有一个重要的变量installedModules
。咱们加载过的模块中的module.exports
对象,会保存在installedModules[moduleId]中。这样下次调用,就能够直接返回module.exports
。
OK,此次就讲到这里,但愿对同窗们学习理解webpack
能有帮助。