ESModule和CommonJS的区别

379 阅读6分钟

前言

在使用原生js时遇到了一些问题,于是衍生出 ESModule和CommonJS规范,现在来讲讲自己了解到的ESModule和CommonJS的区别

正文

CommonJS

一、什么是CommonJS

CommonJS是一种规范,
伴随 nodejs 而诞生的 commonjs 规范。commonjs 规范应用于 nodejs 应用中,在 nodejs 应用中每个文件就是一个模块,拥有自己的作用域,文件中的变量、函数都是私有的,与其他文件相隔离。
简单来说:运行在node环境下的JavaScript代码遵循CommonJS规范,就是定义了一些规则来规范代码。

扩展

//什么是nodejs?  

nodejs就是JavaScript的一个运行环境,我们知道JavaScript可以在浏览器环境运行,  
还有一个就是可以在node环境运行,通常JavaScript都是和HTML搭配使用,并且运行在客户端,  
而Node.js 就是运行在服务端的 JavaScript

二、为什么要有CommonJS

JavaScript在以前主要应用于客户端,在浏览器环境使用,JavaScript的规范存在很多缺陷,这使得难以使用JavaScript开发大型应用,比如:

  • 没有模块系统
  • 标准库较少
  • 没有标准接口
  • 缺乏包管理系统
  • 列表内容 于是CommonJS规范的提出,主要是为了弥补JavaScript没有标准的缺陷,已达到像Python、Ruby和Java那样具备开发大型应用的基础能力,而不是停留在开发浏览器端小脚本程序的阶段。
    所以出现了CommonJS规范

三、CommonJS规范

1.模块化

因为模块化每个文件就是一个模块,有自己的作用域。在一个文件里面定义的变量、函数、类,都是私有的,对其他文件不可见。
例子:

// a.js
var x = 5;
var addX = function (value) {
  return value + x;
};

a.js文件中定义了一个x变量赋值为5,这个变量只能在a.js文件中被找到并使用,在别的文件是不能拿到这个变量的,所以每个文件都是一个模块,并且每个模块都有自己的作用域,不会和其他文件冲突,不会造成变量污染。

// b.js
console.log(a)    //a is not defined

b.js文件中是无法拿到a.js文件中的变量的。

但是很多时候,我们会存在多个js文件,不可能把所有代码放在一个js文件中,这时候当b.js需要使用a.js文件中的变量或者函数时,我们该怎么做?

2.导出和导入

Node内部提供一个Module构建函数。所有模块都是Module的实例。
也就是每一个js文件都是Module的实例
!!! 这是在node环境下

例子:
module.exports属性表示当前模块对外输出的接口,其他文件加载该模块。

//a.js
var x = 5;
var y = 6
module.exports.x = x;
module.exports.y = y;

a.js文件中x变量需要在其他文件中使用,module.exports.x = x;会将x挂载在module.export会作为对象被导出,这样可以多个导出。

//b.js
var example = require('./a.js');
console.log(example); // { x: 5, y: 6 }
console.log(example.x); // 5

导入时使用require将导出文件引入,并且使用变量接收,这时候的example是一个对象,可以使用.的方法获取对象上挂载的导出值。


另一种情况

//a.js
var x = 5;
var y = 6
module.exports = x;
module.exports = y;
//b.js
var example = require('./a.js');
console.log(example); // 6 

如果使用module.exports = x;单一导出,会覆盖。

3.require

Node使用CommonJS模块规范,内置的require命令用于加载模块文件。

require命令的基本功能是,读入并执行一个JavaScript文件,然后返回该模块的exports对象。如果没有发现指定模块,会报错。

使用规则
(1)如果参数字符串以“/”开头,则表示加载的是一个位于绝对路径的模块文件。比如,require('/home/marco/foo.js')将加载/home/marco/foo.js

(2)如果参数字符串以“./”开头,则表示加载的是一个位于相对路径(跟当前执行脚本的位置相比)的模块文件。比如,require('./circle')将加载当前脚本同一目录的circle.js

(3)如果参数字符串不以“./“或”/“开头,则表示加载的是一个默认提供的核心模块(位于Node的系统安装目录中),或者一个位于各级node_modules目录的已安装模块(全局安装或局部安装)。
举例来说,脚本/home/user/projects/foo.js执行了require('bar.js')命令,Node会依次搜索以下文件。

-   /usr/local/lib/node/bar.js
-   /home/user/projects/node_modules/bar.js
-   /home/user/node_modules/bar.js
-   /home/node_modules/bar.js
-   /node_modules/bar.js

这样设计的目的是,使得不同的模块可以将所依赖的模块本地化。

(4)如果参数字符串不以“./“或”/“开头,而且是一个路径,比如require('example-module/path/to/file'),则将先找到example-module的位置,然后再以它为参数,找到后续路径。

(5)如果指定的模块文件没有发现,Node会尝试为文件名添加.js.json.node后,再去搜索。.js件会以文本格式的JavaScript脚本文件解析,.json文件会以JSON格式的文本文件解析,.node文件会以编译后的二进制文件解析。

(6)如果想得到require命令加载的确切文件名,使用require.resolve()方法。

ESModule

一、什么是ESModule

历史上,JavaScript一直没有模块体系(module),无法将一个大程序拆分成互相依赖的小文件,再用简单的方法拼装起来。其他语言都有这项功能,唯独javascript没有,在这之前我们都是借助第三方工具(require.js等)来实现模块化开发,直到ES6 module的出现,成为浏览器和服务器通用的模块解决方案,完全可以取代 CommonJS 和 AMD 规范

目前浏览器基本都是支持ES Module规范,通过给script标签添加type=module标签就可以以 ES Module的标准执行JS代码。

二、ESModule基本特点

  • 每一个模块只加载一次, 并执行一次,再次加载同一文件,直接从内存中读取;
  • 每一个模块内声明的变量都是局部变量, 不会污染全局作用域;
  • 通过export导出模块,通过import导入模块
  • ES6模块只支持静态导入和导出,只可以在模块的最外层作用域使用import和export

三、ESModule规范

1.导入和导出

导出

//a.js
let a = 'a'
let b = 'b'
export { b } //导出b变量
export let c = 'c' //导出c变量
export default a //默认导出a变量

a.js文件导出了a、b两个变量 导入

//b.js
import count from './a.js'
import { b } from './a.js'
import { c } from './a.js'
console.log(b); //b
console.log(c); //c
console.log(count); //a

b.js文件引入了a.js文件导出的内容,

export为普通导出、export default为默认导出
使用默认导出时,引入文件,自定义变量名,引入的就是默认导出内容
但是如果想要引入不是默认导出的内容时,引入时需要引入对应导出的变量名并且加上{},也就是命名导入
上面代码也可以看出,export导出有两种方式,一种是加上{},一种是直接导出一个新定义的变量

ps:

在html中引入js文件时,- 浏览器支持ES Module,可以直接使用type="module"

<script type="module" src="./b.js"></script>

如果浏览器不支持ES Module, 利用webpack等工具转换成ES5后引入(推荐) 否则会报错

image.png

总结

CommonJS

exports = module.exports 用于导出

require 用于导入 导出是引用赋值关系,浅拷贝

模块在第一次被引入的时候,模块中的js代码会被运行一次

模块被多次引入时,会缓存,最终只加载(运行)一次

ES Module

  • export负责将模块内的内容导出
  • import负责从其他模块导入内容

 html文件中引入需要加type="module"

本章完