webpack源码分析—ESModule规范模块的导入与导出详解

前言

上一章分析了CommonJS规范模块的导入与导出,这一章我们来分析ESModule规范模块的导入与导出。

ESModule规范模块的导入与导出

示例代码

// index.js
import data, { param1, param2 } from './module.js'
console.log(data)
console.log(param1)
console.log(param2)
// module.js
export default 'this is default export'
export let param1 = 'export param1'
export let param2 = 'export param2'

在这里插入图片描述

导入分析

在这里插入图片描述
先分析左边的代码,我们希望导入module模块,并取出其中的默认导出变量default和变量param1param2这三个变量,并打印

再分析右边的代码,他调用了一个__webpack_require_.r方法,然后使用__webpack_require__方法导入module模块,并使用一个变量保存,最后打印了这个变量的defaultparam1param2属性

可以看出,实际上它基本执行了左边代码的操作,只是进行了一些额外处理

差异:

  • 调用了__webpack_require_.r方法
  • 使用__webpack_require__方法进行导入
  • 使用变量接收导出内容,并使用变量[key]方式替换原本模块变量的使用

可以看到,不管是commonjs规范,还是esmodule规范,最终导入的方法都是使用__webpack_require__来实现,并且都是通过将变量挂载到exports上来实现模块变量的导出。

但esmodule规范多了一个__webpack_require_.r方法调用,这个方法有什么用呢?

r方法—模块类型标记

来看看r方法的内部逻辑

__webpack_require__.r = function (exports) {
  // 判断当前环境是否支持Symbol语法和Symbol.toStringTag方法
  if (typeof Symbol !== 'undefined' && Symbol.toStringTag) {
  	// 存在则向exports添加一个唯一属性Symbol.toStringTag,值为Module
    Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
  }
  // 给exports添加一个属性__esmodule,值为true
  Object.defineProperty(exports, '__esModule', { value: true });
};

可以看到,r方法其实就是为了给exports去添加一些标识,我个人理解,就是为了去标识导入规范类型和当前环境是否支持es6语法(因为Symbol是es6的新的数据类型)

那么,r方式是给谁打标识呢?r方法接收一个exports参数,从字面意义上我们也可以猜测出来,这其实就是我们需要导出的变量module.exports,且exports应该是一个对象。

实际来看看
在这里插入图片描述

在这里插入图片描述
r方法传入的参数是__webpack_exports__,而第二个参数接收的刚好就是module.exports变量(这里不懂的可以看上一章Commonjs规范的导入和导出)

所以,r方法其实就是给当前模块的导出变量打标识

后面的其实就比较常规了,使用__webpack_require__进行导出,使用变量接收导出的内容,最后使用导出的变量。

导出分析

看完导入,我们再来看看导出,看看它是如何将变量挂载上去的。
在这里插入图片描述
我们可以看到,导出的代码和源代码有很大的不同,有一个r方法,还多了一个d方法,我们来简单分析一下

  • 执行r方法,上面说过,这个是给导出变量做标记
  • 执行了d方法,并传递了一些参数
  • 又执行了一次d方法,传递了其他参数
  • __webpack_exports_上挂载了一个default属性
  • 声明变量param1,param2并赋值

d方法分析

其中只有d方法我们是不知道干什么的,我们再进入到d方法去看看

__webpack_require__.d = function (exports, name, getter) {
  if (!__webpack_require__.o(exports, name)) {
    Object.defineProperty(exports, name, { enumerable: true, get: getter });
  }
}

__webpack_require__.o = function (object, property) {
  return Object.prototype.hasOwnProperty.call(object, property);
};

代码并不复杂,d方法接收三个参数exportsnamegetter。从代码可以分析,exports是一个对象。首先通过o方法判断exports是否存在name变量值的属性,如果没有,则再exports上挂载一个name变量值的属性,并设置其访问器为getter

所以d方法其实就是为传入的exports对象挂载一个name变量值的属性,并设置其访问器为getter

我们再看会原来打包后的代码
在这里插入图片描述
先看第一个d方法的执行
它传递了__webpack_exports__"param1"function () { return param1 }这三个参数:

其中__webpack_exports__就是将来导出变量的一个对象,

"param1"正好就是源代码中exports let param1 = "export param1"中的变量名称,

最后的方法返回了一个param1变量,而param1变量的值对应源代码中exports let param1 = "export param1"导出的值
在这里插入图片描述
结论:对于由exports var param = value导出类型的变量,我们通过d方法来将对于的属性名和属性值挂载到导出对象__webpack_exports__上,而对于export default param导出类型的变量则通过__webpack_exports__['default'] = param来实现导出。

所以最终__webpack_export__对象上就有defaultparam1param2这三个需要导出的属性了。

至于为什么d方法中要使用getter,而不是value,我个人猜测是因为要实现其导出变量实际是导出变量的地址,而不是导出他的值的原因。

以上就是本章的内容了


以上内容仅供学习参考

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值