Javascript-Basic-模块化历史
文章目录

模块化的历史

上古时期 Vanilla JS (1995-2009)

Object Literal Pattern(对象字面量)

1
2
3
4
5
6
7
8
9
10
11
12
function Person(name) {
this.name = name;
}

Person.prototype.talk = function () {
console.log("my name is", this.name);
};


const p = new Person("anson");
p.talk();

特性:

  • 通过 new 关键字将变量控制限制在对象内部

IIFE(Immediately Invoked Function Expression)

1
2
3
4
5
6
7
8
9
// lib.js
(function() {
const base = 10;
this.sumDOM = function(id) {
// 依赖 jQuery
return base + +$(id).text();
}
})();

特性:

  • 使用闭包实现实现自运行匿名函数并控制 Scope

Non-Native Module Format & Module Loader(2009~2015)

CommonJS

exports ( module.exports ) , require , global , __filename , __dirname

1
2
3
4
5
6
7
// lib.js
module.exports.add = function add() {};

// main.js
const { add } = require("./lib.js");
add();

特性:

  • CommonJS 是的 NodeJS 所使用的一种服务端的模块化规范, 它将每一个文件定义为一个 module, 模块必须通过 module.exports 导出对外的变量或接口, 通过 require() 来导入其他模块的输出到当前模块作用域中
  • 使用 require 来导入依赖, exports 来导出接口.
  • 浏览器并不支持 CommonJS, 所以代码需要通过 Babel 等 transpiler 转换为 ES5 才能在浏览器上运行
  • 一开始设计于服务端

AMD & CMD & UMD

define & require

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// AMD
define(['./a', './b'], function(a, b) {
// 加载模块完毕可以使用
a.do();
b.do();
});

// CMD
define(function(require, exports, module) {
// 加载模块
// 可以把 require 写在函数体的任意地方实现延迟加载
var a = require('./a');
a.doSomething();

// 也可以使用 require.async 来延迟加载
require.async('./b', function(b) {
b.doSomething();
});
});

UMD 是将前几种方法的集合, 然后会自动选择可用的模块化方法

EcmaScript Module (ESM) (2015~now)

ESM = EcmaScript Module

ESM 是 ES6 提供的官方 js 模块化方案, import 和 export

1
2
3
4
5
6
7
// lib.mjs
const lib = function() {};
export default lib;

// main.js
import lib from './lib.mjs';

特性:

  • import 和 export
  • 主流浏览器已经支持
  • 默认启用严格模式即 “use strict”;
  • 默认 defer 加载执行;
  • 默认启用 CORS 跨域;
  • 在同一个文档中, 相同的模块只会加载, 执行一次;

CommonJS 和 ESM 的区别

现在基本上大家都用 ESM

  • require/exports 是运行时动态加载, import/export 是静态编译
    • require/exports 加载的是一个对象, 该对象只有在脚本运行完才会生成.
      • 总之就是运行完才会生成
    • 而 ES6 模块不是对象, 它的对外接口只是一种静态定义, 在代码静态解析阶段就会生成.
      • 这个的意思就是在解析的时候就会生成, 不用等它运行完
  • require/exports 输出的是一个值的拷贝, import/export 模块输出的是值的引用
    • 有些时候模块内部的值会造成一些变化,import/export 就会影响到输出之后的变量
  • require/exports 默认不会调用严格模式, import/export 默认调用严格模式

编写支持多种模块格式的库

在 Node 12+ 我们可以使用 package.json 的 exports 字段来为包配置支持不同的模块文件, Node 会根据你使用 import 还是 require 来加载, 返回相应的模块文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// package.json
{
"exports": {
"import": "./lib.mjs",
"require": "./lib.cjs"
}
}

// app.mjs
import { value } from "lib";
console.log("value from mjs", value);

// app.cjs
const value = require("lib").value;
console.log("value from cjs", value);

参考文献