当前位置:   article > 正文

Commonjs 和 Es Module详解

Commonjs 和 Es Module详解

一 前言

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

带上疑问开始今天的分析:

  • 1 Commonjs 和 Es Module 有什么区别 ?
  • 2 Commonjs 如何解决的循环引用问题 ?
  • 3 既然有了 exports,为何又出了 module.exports ? 既生瑜,何生亮 ?
  • 4 require 模块查找机制 ?
  • 5 Es Module 如何解决循环引用问题 ?
  • 6 exports = {} 这种写法为何无效 ?
  • 7 关于 import() 的动态引入 ?
  • 8 Es Module 如何改变模块下的私有变量 ?
  • 9 …

二 模块化

早期 JavaScript 开发很容易存在全局污染依赖管理混乱问题。这些问题在多人开发前端应用的情况下变得更加棘手。我这里例举一个很常见的场景:

js复制代码<body>
  <script src="./index.js"></script>
  <script src="./home.js"></script>
  <script src="./list.js"></script>
</body>
  • 1
  • 2
  • 3
  • 4
  • 5

如上在没有模块化的前提下,如果在 html 中这么写,那么就会暴露一系列问题。

  • 全局污染

没有模块化,那么 script 内部的变量是可以相互污染的。比如有一种场景,如上 ./index.js 文件和 ./list.js 文件为小 A 开发的,./home.js 为小 B 开发的。

小 A 在 index.js中声明 name 属性是一个字符串

js
复制代码var name = '我不是外星人'
  • 1
  • 2

然后小 A 在 list.js 中,引用 name 属性,

js
复制代码console.log(name)
  • 1
  • 2

1.jpg

打印却发现 name 竟然变成了一个函数。刚开始小 A 不知所措,后来发现在小 B 开发的 home.js 文件中这么写道:

js复制代码function name(){
    //...
}
  • 1
  • 2
  • 3

而且这个 name 方法被引用了多次,导致一系列的连锁反应。

上述例子就是没有使用模块化开发,造成的全局污染的问题,每个加载的 js 文件都共享变量。当然在实际的项目开发中,可以使用匿名函数自执行的方式,形成独立的块级作用域解决这个问题。

只需要在 home.js 中这么写道:

js复制代码(function (){
    function name(){
        //...
    }
})()
  • 1
  • 2
  • 3
  • 4
  • 5

2.jpg

这样小 A 就能正常在 list.js 中获取 name 属性。但是这只是一个 demo ,我们不能保证在实际开发中情况会更加复杂。所以不使用模块开发会暴露出很多风险。

  • 依赖管理

依赖管理也是一个难以处理的问题。还是如上的例子,正常情况下,执行 js 的先后顺序就是 script 标签排列的前后顺序。那么如何三个 js 之间有依赖关系,那么应该如何处理呢?

假设三个 js 中,都有一个公共方法 fun1fun2fun3。三者之间的依赖关系如下图所示。

  • 下层 js 能调用上层 js 的方法,但是上层 js 无法调用下层 js 的方法。

3.jpg

所以就需要模块化来解决上述的问题,今天我们就重点讲解一下前端模块化的两个重要方案:CommonjsEs Module

Commonjs

Commonjs 的提出,弥补 Javascript 对于模块化,没有统一标准的缺陷。nodejs 借鉴了 Commonjs 的 Module ,实现了良好的模块化管理。

目前 commonjs 广泛应用于以下几个场景:

  • Node 是 CommonJS 在服务器端一个具有代表性的实现;
  • Browserify 是 CommonJS 在浏览器中的一种实现;
  • webpack 打包工具对 CommonJS 的支持和转换;也就是前端应用也可以在编译之前,尽情使用 CommonJS 进行开发。

1 commonjs 使用与原理

在使用 规范下,有几个显著的特点。

  • commonjs 中每一个 js 文件都是一个单独的模块,我们可以称之为 module;
  • 该模块中,包含 CommonJS 规范的核心变量: exports、module.exports、require;
  • exports 和 module.exports 可以负责对模块中的内容进行导出;
  • require 函数可以帮助我们导入其他模块(自定义模块、系统模块、第三方库模块)中的内容;
commonjs 使用初体验

导出:我们先尝试这导出一个模块:

hello.js

js复制代码let name = '《React进阶实践指南》'
module.exports = function sayName  (){
    return name
}
  • 1
  • 2
  • 3
  • 4

导入:接下来简单的导入:

home.js

js复制代码const sayName = require('./hello.js')
module.exports = function say(){
    return {
        name:sayName(),
        author:'我不是外星人'
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

如上就是 Commonjs 最简单的实现,那么暴露出两个问题:

  • 如何解决变量污染的问题。
  • module.exports,exports,require 三者是如何工作的?又有什么关系?
commonjs 实现原理

首先从上述得知每个模块文件上存在 moduleexportsrequire三个变量,然而这三个变量是没有被定义的,但是我们可以在 Commonjs 规范下每一个 js 模块上直接使用它们。在 nodejs 中还存在 __filename__dirname 变量。

如上每一个变量代表什么意思呢:

  • module 记录当前模块信息。
  • require 引入模块的方法。
  • exports 当前模块导出的属性

在编译的过程中,实际 Commonjs 对 js 的代码块进行了首尾包装, 我们以上述的 home.js 为例子

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/AllinToyou/article/detail/130272
推荐阅读
相关标签