上一篇文章介绍了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的。