CommonJS 、ES6模块化规范区别

CommonJS 、ES6模块化规范区别

一、什么是CommonJS

我们知道Node.js的实现让js也可以成为后端开发语言,
但在早先Node.js开发过程中,它的作者发现在js中并没有像其他后端语言一样有包引入和模块系统的机制。
这就意味着js的所有变量,函数都在全局中定义。这样不但会污染全局变量,更会导致暴露函数内部细节等问题。
CommonJS组织也意识到了同样的问题,于是 CommonJS组织创造了一套js模块系统的规范。我们现在所说的CommonJS指的就是这个规范。

CommonJS 规定:

(1)每个模块内部,module 变量代表当前模块。

(2)module 变量是一个对象,它的 exports 属性(即 module.exports)是对外的接口。

(3)加载某个模块,其实是加载该模块的 module.exports 属性。require() 方法用于加载模块。

二、两者区别

在 ES6 之前,社区制定了一些模块加载方案,最主要的有 CommonJS 和 AMD 两种。前者用于服务器,后者用于浏览器。ES6 在语言标准的层面上,实现了模块功能,而且实现得相当简单,完全可以取代 CommonJS 和 AMD 规范,成为浏览器和服务器通用的模块解决方案。
ES6 模块的设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonJS 和 AMD 模块,都只能在运行时确定这些东西。

1.语法上的差异

  • CommonJS 模块是 Node.js 专用的,与 ES6 模块不兼容。而ES6模块化在浏览器和node.js中都可以用。
  • 语法上面,两者最明显的差异是,CommonJS 模块使用require()和module.exports,ES6 模块使用import和export。
  • 在node.js使用模块化,需要将 CommonJS 脚本的后缀名都改成.cjs,ES6 模块采用.mjs后缀文件名。或者修改package.son里面的文件,type字段为module或commonjs。

2. 什么是运行时加载呢?

// CommonJS模块
let { stat, exists, readfile } = require('fs');

// 等同于
let _fs = require('fs');
let stat = _fs.stat;
let exists = _fs.exists;
let readfile = _fs.readfile;

以上代码的实质是整体加载fs模块(即加载fs的所有方法),生成一个对象(_fs),然后再从这个对象上面读取 3 个方法。这种加载称为“运行时加载”,因为只有运行时才能得到这个对象,导致完全没办法在编译时做“静态优化”。

3. 什么是编译时加载或静态加载呢?

// ES6模块
import { stat, exists, readFile } from 'fs';

这个代码的实质是从fs模块加载 3 个方法,其他方法不加载。这种加载称为“编译时加载”或者静态加载,即 ES6 可以在编译时就完成模块加载,效率要比 CommonJS 模块的加载方式高。当然,这也导致了没法引用 ES6 模块本身,因为它不是对象。

4.在 Node.js 中使用模块化,ES6 模块与 CommonJS 模块差异

  • CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。
    注意:CommonJS 模块输出的是值的拷贝,也就是说,一旦输出一个值,模块内部的变化就影响不到这个值,ES6 模块是动态引用,并且不会缓存值,模块里面的变量绑定其所在的模块。
  • CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。
    原因:CommonJS 加载的是一个对象(即module.exports属性),该对象只有在脚本运行完才会生成。而 ES6 模块不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成。
  • CommonJS 模块的require()是同步加载模块,ES6 模块的import命令是异步加载,有一个独立的模块依赖的解析阶段。
    commonjs举例:
// lib.js
var counter = 3;
function incCounter() {
  counter++;
}
module.exports = {
  counter: counter,
  incCounter: incCounter,
};

// main.js
var mod = require('./lib');

console.log(mod.counter);  // 3
mod.incCounter();
console.log(mod.counter); // 3
//lib.js模块加载以后,它的内部变化就影响不到输出的mod.counter了。
//这是因为mod.counter是一个原始类型的值,会被缓存。
//除非写成一个函数,才能得到内部变动后的值。
// lib.js
var counter = 3;
function incCounter() {
  counter++;
}
module.exports = {
  get counter() {
    return counter
  },
  incCounter: incCounter,
};

ES6举例:

// lib.js
export let counter = 3;
export function incCounter() {
  counter++;
}

// main.js
import { counter, incCounter } from './lib';
console.log(counter); // 3
incCounter();
console.log(counter); // 4
//ES6 模块输入的变量counter是活的,完全反应其所在模块lib.js内部的变化。

三、在 Node.js 中使用模块化

1. 在Node.js 中使用ES6模块

Node.js 要求 ES6 模块采用.mjs后缀文件名。如果不希望将后缀名改成.mjs,可以在项目的package.json文件中,指定type字段为module。

{
   "type": "module"
}

2. 在Node.js 中使用commonjs模块

Node.js 要求 ES6 模块采用.cjs后缀文件名。
如果不希望将后缀名改成.cjs,可以在项目的package.json文件中,指定type字段为commonjs。

{
   "type": "commonjs"
}

你可能感兴趣的:(前端,es6,node.js,js,es6,node.js,前端)