CommonJS 与 ES6 module 对比

460 阅读3分钟

commonjs 与 es6 module 是现如今最常用的模块化导入导出方式,本文是作者阅读了一些书籍整理得来,特记录以加深理解与思考。

1. 源来

CommonJS 诞生于2009年,目的是用来解决非浏览器宿主环境的模块引入问题,后来在nodejs 中得以使用,nodejs 对其细节加以实现,慢慢成为nodejs中的模块规范。 其关键字 为 module.exportsrequire

ES6 module 为ECMA 官方出品,于2015年推出,为es及js语言中官方的模块化规范。目前广泛使用于前端代码中。其中关键字为 exportexport defaultimport

2. 使用方式

2.1 Commonjs 定义方式

nodejs 会在当前js文件中注入 module 的对象,其中包括exports作为module的一个属性,单独的exports字段可以看做是module.exports 的赋值如。 var exports = module.exports;

如下是一些使用方式。

// math.js
// 方法一 使用一个对象字面量的形式去赋值 module.exports
module.exports = {
    name: "math",
    add: function (a, b) {return a + b;};
}
// 方法二
module.exports.name = "math";
module.exports.add = function add(a, b) {return a + b;};

// 方法三 使用exports. 的形式去单个定义一个变量
exports.name = "math";
exports.add = function add(a, b) {return a + b;};

// 方法四  错误使用方式,正如上面的引言,exports若是完全赋值,就会失去module对象的引用从而使用对该对象的导出。
exports = {name: "math", add: function add(a, b) {return a + b};};

*** 补充:module.exports 可以不放在js文件末尾,如下,但是不建议这样做。

module.exports = {
    name: "math",
    add: function (a, b) {return a + b;};
}
console.log('this is math api.');

2.2 ES6 Module 导出

ES6 module 导出分为两种情况 一种是默认导出,一种是 命名导出。

// 方法一 默认导出
export default {
    name: "math",
    add: function(a, b) {return a + b;};
}

// 方法二 单个导出
export const name = "math";
export const add = function(a, b){return a + b;};

// 方法三 命名导出
const name = "math";
const add = function(a, b){return a + b;};
export {
    name: name,
    sum: add // 命名
}

// 复合导出(默认导出 和 命名导出)
export const name = "math";
export const add = function(a, b){return a + b;};



2.3 CommonJS 导入

应对于上述的导出,相应的也有导入方案,这里依赖的是 require 关键字。


// 方式一
var math = require("math.js");
math.name; // 'math';
math.add(2,3); // 5

// 方式二
if (true) {
    var math = require("math.js");
    math.name; // 'math';
    math.add(2,3); // 5
}

// 方式三
var url = './math.js';
var math = require(url);
math.name; // 'math';
math.add(2,3); // 5


2.4 ES6 module 导入

// 方式一 默认导出
// math.js
export default {name: 'math', add: function(a, b) {return a + b}};

import Math from "./math.js";
Math.name; // 'math'
Math.add(2, 3); // 5

// 方式二 命名导出 部分导入
// math.js
const name = "math";
const add = function(a, b){return a + b;};
export {
    name: name,
    sum: add // 命名
};
import {name, add} from "./math.js";
add(2, 3); // 5

// 方式三 命名导出 全部导入
const name = "math";
const add = function(a, b){return a + b;};
export {
    name: name,
    sum: add // 命名
};
import * as Math from "./math.js";
Math.name; // 'math';
Math.add(2, 3); // 5

// 方式四 默认导出 命名导出
import React, {Component} from "react";

// 非顶级作用域不支持 
if (true) {
    import math from "./math.js";
}

3. 二者对比

  1. commonjs是一种动态引入的方式,只能在运行时使用,所以其require的路径可以变量,而es6-module 是一种静态的引入方式,所以其发生在js代码的编译时。静态的好处是,我在编译的时候就知道你的类型,所以类型推断,检查都可以使用。并且从性能上讲,发生在运行时的成本明显要高于编译时期。
  2. top level es6-module 只支持在文件顶级作用域引入。
  3. es6 module 默认严格模式,所以一些被移除的语法可能会报错,例如 with。
  4. es6 module 可以局部引入,便于做 tree-shaking 性能优化。

4.最后

比来比去,突然想到现如今的 es6 module 还是依赖于webpack babel之类的工具做编译生成es5的代码,所以我们可能要学习的是将es6-module之类的编译为 commonjs的方法,当然灵活使用最新、最官方的技术也是我们应该追求的。毕竟喜新厌旧啊。