上一篇文章介紹了webpack對commonjs模塊的支持(若是你還沒讀過,建議你先閱讀),這篇文章來探究一下,webpack是如何支持es模塊的。webpack
咱們依然寫兩個文件,m.js文件用es模塊的方式export
一個default
函數和一個foo
函數,index.js import
該模塊,具體代碼以下:es6
// m.js 'use strict'; export default function bar () { return 1; }; export function foo () { return 2; }
// index.js 'use strict'; import bar, {foo} from './m'; bar(); foo();
webpack配置沒有變化,依然以index.js做爲入口:web
var path = require("path"); module.exports = { entry: path.join(__dirname, 'index.js'), output: { path: path.join(__dirname, 'outs'), filename: 'index.js' }, };
在根目錄下執行webpack
,獲得通過webpack打包的代碼以下(去掉了沒必要要的註釋):segmentfault
(function(modules) { // webpackBootstrap // The module cache var installedModules = {}; // The require function 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] = { i: moduleId, l: false, exports: {} }; // Execute the module function modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); // Flag the module as loaded module.l = true; // Return the exports of the module return module.exports; } // expose the modules object (__webpack_modules__) __webpack_require__.m = modules; // expose the module cache __webpack_require__.c = installedModules; // define getter function for harmony exports __webpack_require__.d = function(exports, name, getter) { if(!__webpack_require__.o(exports, name)) { Object.defineProperty(exports, name, { configurable: false, enumerable: true, get: getter }); } }; // getDefaultExport function for compatibility with non-harmony modules __webpack_require__.n = function(module) { var getter = module && module.__esModule ? function getDefault() { return module['default']; } : function getModuleExports() { return module; }; __webpack_require__.d(getter, 'a', getter); return getter; }; // Object.prototype.hasOwnProperty.call __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; // __webpack_public_path__ __webpack_require__.p = ""; // Load entry module and return exports return __webpack_require__(__webpack_require__.s = 0); }) ([ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; Object.defineProperty(__webpack_exports__, "__esModule", { value: true }); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__m__ = __webpack_require__(1); Object(__WEBPACK_IMPORTED_MODULE_0__m__["a" /* default */])(); Object(__WEBPACK_IMPORTED_MODULE_0__m__["b" /* foo */])(); }), (function(module, __webpack_exports__, __webpack_require__) { "use strict"; /* harmony export (immutable) */ __webpack_exports__["a"] = bar; /* harmony export (immutable) */ __webpack_exports__["b"] = foo; function bar () { return 1; }; function foo () { return 2; } }) ]);
上一篇文章已經分析過了,webpack生成的代碼是一個IIFE,這個IIFE完成一系列初始化工做後,就會經過__webpack_require__(0)
啓動入口模塊。模塊化
咱們首先來看m.js模塊是如何實現es的export
的,被webpack轉換後的m.js代碼以下:函數
__webpack_exports__["a"] = bar; __webpack_exports__["b"] = foo; function bar () { return 1; }; function foo () { return 2; }
其實一眼就能看出來,export default和export都被轉換成了相似於commonjs的exports.xxx
,這裏也已經不區分是否是default export了,全部的export對象都是__webpack_exports__
的屬性。ui
咱們繼續來看看入口模塊,被webpack轉換後的index.js代碼以下:prototype
Object.defineProperty(__webpack_exports__, "__esModule", { value: true }); var __WEBPACK_IMPORTED_MODULE_0__module__ = __webpack_require__(1); Object(__WEBPACK_IMPORTED_MODULE_0__m__["a" /* default */])(); Object(__WEBPACK_IMPORTED_MODULE_0__m__["b" /* foo */])();
index模塊首先經過Object.defineProperty
在__webpack_exports__
上添加屬性__esModule
,值爲true
,代表這是一個es模塊。在目前的代碼下,這個標記是沒有做用的,至於在什麼狀況下須要判斷模塊是否es模塊,後面會分析。code
而後就是經過__webpack_require__(1)
導入m.js模塊,再而後經過module.xxx
獲取m.js中export的對應屬性。注意這裏有一個重要的點,就是全部引入的模塊屬性都會用Object()包裝成對象,這是爲了保證像Boolean、String、Number這些基本數據類型轉換成相應的類型對象。對象
咱們前面分析的都是commonjs模塊對commonjs模塊的導入,或者es模塊對es模塊的導入,那麼若是是es模塊對commonjs模塊的導入會是什麼狀況呢,反過來又會如何呢?
其實咱們前面說到的__webpack_exports__. __esModule = true
就是針對這種狀況的解決方法。
下面用具體代碼來解釋一下,首先修改m.js和index.js代碼以下:
// m.js 'use strict'; exports.foo = function () { return 1; }
// index.js 'use strict'; import m from './m'; m.foo();
從新執行webpack後生成的代碼以下(只截取IIFE的參數部分):
[ /* 0 */ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; Object.defineProperty(__webpack_exports__, "__esModule", { value: true }); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__m__ = __webpack_require__(1); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__m___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0__m__); __WEBPACK_IMPORTED_MODULE_0__m___default.a.foo(); }), /* 1 */ (function(module, exports, __webpack_require__) { "use strict"; exports.foo = function () { return 1; } }) ]
m.js轉換後的代碼跟轉換前的代碼基本沒有變化,都是用webpack提供的exports
進行模塊導出。可是index.js有一點不一樣,主要是多了一行代碼:
var __WEBPACK_IMPORTED_MODULE_0__m___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0__m__);
這段代碼做用是什麼呢,看一下__webpack_require__.n
的定義就知道了:
// getDefaultExport function for compatibility with non-harmony modules __webpack_require__.n = function(module) { var getter = module && module.__esModule ? function getDefault() { return module['default']; } : function getModuleExports() { return module; }; __webpack_require__.d(getter, 'a', getter); return getter; };
__webpack_require__.n
會判斷module是否爲es模塊,當__esModule
爲true的時候,標識module爲es模塊,那麼module.a
默認返回module.default
,不然返回module
。
具體實現則是經過 __webpack_require__.d
將具體操做綁定到屬性a
的getter方法上的。
那麼,當經過es模塊的方式去import
一個commonjs規範的模塊時,就會把require獲得的module進行一層包裝,從而兼容兩種狀況。
至於經過commonjs去require
一個es模塊的狀況,原理相同,就不過多解釋了。
webpack對於es模塊的實現,也是基於本身實現的__webpack_require__
和__webpack_exports__
,裝換成相似於commonjs的形式。對於es模塊和commonjs混用的狀況,則須要經過__webpack_require__.n
的形式作一層包裝來實現。
下一篇webpack模塊化原理-Code Splitting,會繼續來分析webpack是如何經過動態import
和module.ensure
實現Code Splitting的。