瀏覽器自己不支持模塊化css
是一種在線"編譯"模塊的方案,至關於在頁面上加載一個 CMD/AMD 解釋器。這樣瀏覽器就認識了 define、exports、module 這些東西。也就實現了模塊化。html
你在本地直接寫JS,不論是 AMD / CMD / ES6 風格的模塊化,它都能認識,而且編譯成瀏覽器認識的JS。前端
ES6在語言標準的層面上, 實現了模塊功能, 並且實現得至關簡單, 徹底能夠取代CommonJS和AMD規範, 是瀏覽器和服務器通用的模塊解決方案node
從amd cmd 到 (commonjs), 再到瀏覽器支持模塊化es6。webpack
瀏覽器不兼容Commonjs的根本緣由,是由於缺乏NodeJs的4個環境變量git
讓瀏覽器支持,這幾個環境變量。咱們來定義這幾個變量。es6
var module = {
exports:{}
};
(function(module, exports) {
exports.multiply = function (n) { return n * 1000 };
}(module, module.exports))
var f = module.exports.multiply;
f(5) // 5000
複製代碼
上面代碼向一個當即執行函數提供 module 和 exports 兩個外部變量,模塊就放在這個當即執行函數裏面。模塊的輸出值放在 module.exports 之中,這樣就實現了模塊的加載。github
https://github.com/ruanyf/tiny-browser-require/blob/master/require.js
複製代碼
webpack是如何支持commonjs的?web
咱們都知道,webpack做爲一個構建工具,解決了前端代碼缺乏模塊化能力的問題。咱們寫的代碼,通過webpack構建和包裝以後,可以在瀏覽器以模塊化的方式運行。這些能力,都是由於webpack對咱們的代碼進行了一層包裝,本文就以webpack生成的代碼入手,分析webpack是如何實現模塊化的。segmentfault
PS: webpack的模塊不只指js,包括css、圖片等資源均可以以模塊看待,但本文只關注js。
webpack打包的代碼,總體能夠簡化成下面的結構:
(function (modules) {/* 省略函數內容 */})
([
function (module, exports, __webpack_require__) {
/* 模塊a.js的代碼 */
},
function (module, exports, __webpack_require__) {
/* 模塊b.js的代碼 */
}
]);
複製代碼
能夠看到,整個打包生成的代碼是一個IIFE(當即執行函數),函數內容咱們待會看,咱們先來分析函數的參數。
函數參數是咱們寫的各個模塊組成的數組,只不過咱們的代碼,被webpack包裝在了一個函數的內部,也就是說咱們的模塊,在這裏就是一個函數。爲何要這樣作,是由於瀏覽器自己不支持模塊化,那麼webpack就用函數做用域來hack模塊化的效果。
若是你debug過node代碼,你會發現同樣的hack方式,node中的模塊也是函數,跟模塊相關的參數exports、require,或者其餘參數__filename和__dirname等都是經過函數傳值做爲模塊中的變量,模塊與外部模塊的訪問就是經過這些參數進行的,固然這對開發者來講是透明的。
一樣的方式,webpack也控制了模塊的module、exports和require,那麼咱們就看看webpack是如何實現這些功能的。
下面是摘取的函數內容,並添加了一些註釋:
// 一、模塊緩存對象
var installedModules = {};
// 二、webpack實現的require
function __webpack_require__(moduleId) {
// 三、判斷是否已緩存模塊
if(installedModules[moduleId]) {
return installedModules[moduleId].exports;
}
// 四、緩存模塊
var module = installedModules[moduleId] = {
i: moduleId,
l: false,
exports: {}
};
// 五、調用模塊函數
modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
// 六、標記模塊爲已加載
module.l = true;
// 七、返回module.exports
return module.exports;
}
// 八、require第一個模塊
return __webpack_require__(__webpack_require__.s = 0);
複製代碼
webpack對於es模塊的實現,也是基於本身實現的__webpack_require__ 和__webpack_exports__ ,裝換成相似於commonjs的形式。對於es模塊和commonjs混用的狀況,則須要經過__webpack_require__.n的形式作一層包裝來實現。
webpack的模塊化不只支持commonjs和es module,還能經過code splitting實現模塊的動態加載。根據wepack官方文檔,實現動態加載的方式有兩種:import和require.ensure。
webpack經過__webpack_require__.e函數實現了動態加載,再經過webpackJsonp函數實現異步加載回調,把模塊內容以promise的方式暴露給調用方,從而實現了對code splitting的支持。