上一篇文章介绍了webpack对commonjs模块的支持(如果你还没读过,建议你先阅读),这篇文章来探究一下,webpack是如何支持es模块的。

准备

我们依然写两个文件,m.js文件用es模块的方式export一个default函数和一个foo函数,index.js import该模块,具体代码如下:

// 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作为入口:

var path = require("path");
module.exports = {entry: path.join(__dirname, 'index.js'),output: {path: path.join(__dirname, 'outs'),filename: 'index.js'},
};

在根目录下执行webpack,得到经过webpack打包的代码如下(去掉了不必要的注释):

(function(modules) { // webpackBootstrap// The module cachevar installedModules = {};// The require functionfunction __webpack_require__(moduleId) {// Check if module is in cacheif(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 functionmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);// Flag the module as loadedmodule.l = true;// Return the exports of the modulereturn 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 exportsreturn __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__的属性。

我们继续来看看入口模块,被webpack转换后的index.js代码如下:

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模块,后面会分析。

然后就是通过__webpack_require__(1)导入m.js模块,再然后通过module.xxx获取m.js中export的对应属性。注意这里有一个重要的点,就是所有引入的模块属性都会用Object()包装成对象,这是为了保证像Boolean、String、Number这些基本数据类型转换成相应的类型对象。

commonjs与es6 module混用

我们前面分析的都是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是如何通过动态importmodule.ensure实现Code Splitting的。

webpack模块化原理-ES module相关推荐

  1. Webpack模块化原理简析

    webpack模块化原理简析 1.webpack的核心原理 一切皆模块:在webpack中,css,html.js,静态资源文件等都可以视作模块:便于管理,利于重复利用: 按需加载:进行代码分割,实现 ...

  2. JS JavaScript模块化(ES Module/CommonJS/AMD/CMD)

    前言 前端开发中,起初只要在script标签中嵌入几十上百行代码就能实现一些基本的交互效果,后来js得到重视,应用也广泛起来了, jQuery,Ajax,Node.Js,MVC,MVVM等的助力也使得 ...

  3. 模块化:ES Module与commonJS

    模块化以及模块化开发: 模块化开发的目的是将程序划分成一个个小的结构,这个结构有属于自己的代码逻辑,有自己的作用域,不会影响到其他的结构,这个结构希望暴露的变量,函数,对象给其他结构使用,也可以通过某 ...

  4. 前端模块化详解(CommonJS、AMD、CMD、ES Module)

    大家好,我是一碗周,一个不想被喝(内卷)的前端.如果写的文章有幸可以得到你的青睐,万分有幸~ 写在前面 随着前端项目的不断复杂,代码日益膨胀,项目的维护难度随之越来越大,此时模块化也就相继的出现了,本 ...

  5. ES Module原理详解

    ES Module原理详解 一.ES Modules如何工作 流程简析 二.模块加载 1.构造 2.实例化 3.求值 总结 参考 ES Module 系列: ES Module使用详解 ES Modu ...

  6. 前端模块化- ES Module 和 CommonJS 的使用

    ES Module 导入 导入的值不能重新赋值,类似于使用 const 声明过一样. 命名导入 导入特定项 import { something } from './module.js'; 导入特定项 ...

  7. es module 和 commonjs 模块化实践

    1.当export default 导出的值是基本数据类型时,其值是不能被同步修改的. 举个例子,我们新建文件: base.js let name = 'Joker'; export const fn ...

  8. 面试题:Commonjs 和 Es Module区别

    一 前言 今天我们来深度分析一下 Commonjs 和 Es Module,希望通过本文的学习,能够让大家彻底明白 Commonjs 和 Es Module 原理,能够一次性搞定面试中遇到的大部分有关 ...

  9. 请说说CommonJS和ES module的区别

    CommonJS 1.module代表当前模块 在CommonJS中,一个文件就是一个模块,模块中的变量.函数.类都是私有的外部不可以访问,并规定module代表当前模块,exports是对外的接口. ...

最新文章

  1. html链接伪类设置鼠标悬停,链接伪类可以控制超链接的样式吗?是怎样实现的?...
  2. 383. Ransom Note/691. Stickers to Spell Word-- String, Map, back tracking-- 未完待续
  3. 机器学习算法推导的较好例子
  4. opencv python BRIEF描述子
  5. 浅析企业网站从何开始了解百度蜘蛛?
  6. 用viewport进行布局,将页面分成了三分,我想实现弹出的window窗口在整个viewport上...
  7. Ubuntu 14.04 安装Visual studio Code
  8. java多线程之CountDownLatch倒数闸门
  9. x86异常处理与中断机制(1)概述中断的来源和处理方式
  10. “富豪相亲大会”究竟迷失了什么?
  11. 138. 复制带随机指针的链表
  12. Java序列化技术即将被废除!!!
  13. 大数据时代下,数据感知在数据质量管理系统中的应用
  14. python爬虫+selenium模拟点击+网页内容需要点击打开
  15. 原生 js 实现点击按钮复制文本
  16. 88e1111的1000base-x to copper(GBIC)配置及使用
  17. Flutter杂症(couldn't find libflutter.so)
  18. PCB 设计 3. 原理图及其封装制作
  19. 无线怎么联系不上服务器,无线路由器设置好后联系不到服务器
  20. 【BLE】蓝牙Profile

热门文章

  1. 具有多个单元格类型的iOS UITableView
  2. AppBarLayout中的Android TabLayout
  3. Liferay教程– Liferay门户Portlet教程
  4. 使用Adobe Acrobat为PDF文件添加签名(图片+签名)
  5. [BalticOI2014]Friends/[BZOJ4287]新三个和尚
  6. Hadoop_28_MapReduce_自定义 inputFormat
  7. 无问西东,哪怕重头来过
  8. Glide 加载图片背景变绿
  9. Python数据可视化2.3 体育案例
  10. 在Java中获取系统属性