wepack的模塊化原理及配置方法
我們都知道,webpack的特點之一就在於其的模塊化,將各個文件都使用loader功能轉換為js文件,並將其模塊化,那麽其模塊化的原理是什麽呢?
首先我們需要了解CommonJS規範,以及AMD、CMD、UMD規範都是什麽及其原理。
首先,CommonJS是在服務器端用於模塊化的規範,因為是同步的,所以只能在服務器端實現,而瀏覽器端因為缺少node的四個字段:
- module
- exports
- require
- global
// foobar.js //私有變量 var test = 123; //公有方法 function foobar () { this.foo = function() { // do someing ... } this.bar = function () { //do someing ... } } //exports對象上的方法和變量是公有的 var foobar = new foobar(); exports.foobar = foobar; //require方法默認讀取js文件,所以可以省略js後綴 var test = require(‘./boobar‘).foobar; test.bar();
因為沒辦法在瀏覽器使用這個規範,所有就用了AMD規範和CMD規範。
AMD規範是RequireJS 在推廣過程中對模塊定義的規範化產出的,異步使用回調來實現模塊化:
通過數組引入依賴 ,回調函數通過形參傳入依賴 define([‘someModule1‘, ‘someModule2’], function (someModule1, someModule2) { function foo () { /// someing someModule1.test(); } return {foo: foo} }); AMD規範允許輸出模塊兼容CommonJS規範,這時define方法如下: define(function (require, exports, module) { var reqModule = require("./someModule"); requModule.test(); exports.asplode = function () { //someing } });
而CMD是SeaJS 在推廣過程中對模塊定義的規範化產出的
CMD和AMD的區別有以下幾點:
1.對於依賴的模塊AMD是提前執行,CMD是延遲執行。不過RequireJS從2.0開始,也改成可以延遲執行。
2.CMD推崇依賴就近,AMD推崇依賴前置。
//AMD define([‘./a‘,‘./b‘], function (a, b) { //依賴一開始就寫好 a.test(); b.test(); }); //CMD define(function (requie, exports, module) { //依賴可以就近書寫 var a = require(‘./a‘); a.test(); ... //軟依賴 if (status) { var b = requie(‘./b‘); b.test(); } });
那麽我們接下來再看下webpack的模塊化是如何去實現的:
當我將一個依賴於main.js的entry.js打包的時候,整理其打包後的文件可以看到其結構如下:
(function (modules) {/* 省略函數內容 */}) ([ function (module, exports, __webpack_require__) { /* 模塊index.js的代碼 */ }, function (module, exports, __webpack_require__) { /* 模塊bar.js的代碼 */ } ]);
其中函數內容:
// 1、模塊緩存對象 var installedModules = {}; // 2、webpack實現的require function __webpack_require__(moduleId) { // 3、判斷是否已緩存模塊 if(installedModules[moduleId]) { return installedModules[moduleId].exports; } // 4、緩存模塊 var module = installedModules[moduleId] = { i: moduleId, l: false, exports: {} }; // 5、調用模塊函數 modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); // 6、標記模塊為已加載 module.l = true; // 7、返回module.exports return module.exports; } // 8、require第一個模塊 return __webpack_require__(__webpack_require__.s = 0);
我們可以看出:
1.首先webpack聲明了一個installedModules對象,用於緩存模塊
2.聲明了__webpack_require__內部函數,用於實現require功能,首先檢查緩存中是否存在要引用的模塊,如有的話return緩存中的該模塊,否則初始化該模塊,再返回該模塊
3.用完成後,模塊標記為已加載。
require入口模塊時,入口模塊會收到收到三個參數,下面是入口模塊代碼:
function(module, exports, __webpack_require__) { "use strict"; var bar = __webpack_require__(1); bar(); }
webpack傳入的第一個參數module
是當前緩存的模塊,包含當前模塊的信息和exports
;第二個參數exports
是module.exports
的引用,這也符合commonjs的規範;第三個__webpack_require__
則是require
的實現。
在我們的模塊中,就可以對外使用module.exports
或exports
進行導出,使用__webpack_require__
導入需要的模塊,代碼跟commonjs完全一樣。
這樣,就完成了對第一個模塊的require,然後第一個模塊會根據自己對其他模塊的require,依次加載其他模塊,最終形成一個依賴網狀結構。webpack管理著這些模塊的緩存,如果一個模塊被require多次,那麽只會有一次加載過程,而返回的是緩存的內容,這也是commonjs的規範。
這樣就完成了webpack hack commonjs的過程。
wepack的模塊化原理及配置方法