瀏覽器模塊化之路(一),瞭解一下?

解決什麼問題

瀏覽器自己不支持模塊化css

怎麼解決

  1. seajs / require

是一種在線"編譯"模塊的方案,至關於在頁面上加載一個 CMD/AMD 解釋器。這樣瀏覽器就認識了 define、exports、module 這些東西。也就實現了模塊化。html

  1. webpack (預編譯模塊的方案)「把模塊轉換成函數」

你在本地直接寫JS,不論是 AMD / CMD / ES6 風格的模塊化,它都能認識,而且編譯成瀏覽器認識的JS。前端

  1. es6

ES6在語言標準的層面上, 實現了模塊功能, 並且實現得至關簡單, 徹底能夠取代CommonJS和AMD規範, 是瀏覽器和服務器通用的模塊解決方案node

瀏覽器模塊化發展過程

從amd cmd 到 (commonjs), 再到瀏覽器支持模塊化es6。webpack

瀏覽器加載 CommonJS 模塊的實現

  • IIFE
  • webpack的實現

IIFE

瀏覽器不兼容Commonjs的根本緣由,是由於缺乏NodeJs的4個環境變量git

  • require
  • exports
  • module
  • global

解決辦法

讓瀏覽器支持,這幾個環境變量。咱們來定義這幾個變量。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的實現

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);


複製代碼

參考

  1. segmentfault.com/a/119000001…
  2. www.ruanyifeng.com/blog/2015/0…

總結

  • 瀏覽器模塊化,從模塊解釋器到模塊打包器,到瀏覽器支持模塊化
  • webpack把每個模塊文件打包成一個函數,固然也有多個模塊文件合併成一個函數的處理方式
  • 由於瀏覽器自己不支持模塊化,因此webpack實現了模塊化的功能,把咱們commonjs寫的模塊轉成了函數(webpack就用函數做用域來hack模塊化的效果)
  • webpack 對於commonjs與es6模塊混寫的處理,後續分析

後續文章

1. webpack是如何支持es模塊的

webpack對於es模塊的實現,也是基於本身實現的__webpack_require__ 和__webpack_exports__ ,裝換成相似於commonjs的形式。對於es模塊和commonjs混用的狀況,則須要經過__webpack_require__.n的形式作一層包裝來實現。

2. webpack的 code splitting經過promise實現模塊的動態加載

webpack的模塊化不只支持commonjs和es module,還能經過code splitting實現模塊的動態加載。根據wepack官方文檔,實現動態加載的方式有兩種:import和require.ensure。

webpack經過__webpack_require__.e函數實現了動態加載,再經過webpackJsonp函數實現異步加載回調,把模塊內容以promise的方式暴露給調用方,從而實現了對code splitting的支持。

相關文章
相關標籤/搜索