Skip to content
大纲

开发 npm 包基本知识-js模块化的作用及来由

JS模块化发展历程,CommonJS、AMD、CMD、UMD、ES6模块化

0. 命名空间(“伪”模块化时代)和 IIFE (Immediately Invoked Function Expression) 立即调用函数表达式

js
//命名空间模式
var namespace = {};
namespace.add = function (a, b) {
  console.log(a + b);
};

namespace.add(1, 2);

//IIFE (Immediately Invoked Function Expression)
var utils = (function () {
  var module = {};
  module.multiply = function (a, b) {
    console.log(a * b);
  };
  return module;
})();

utils.mutiply(1, 2);

这种模式需要开发人员知道正确的顺序,比如先执行 vue,才能执行调用 vue 的方法

html
<script src="./vue.js"></script>
<script src="./main.js"></script>

1. 2009 年提出的 - CommonJS 也叫 cjs [ˈkɑːmən]

2009年Mozillag工程师发起CommonJS规范,然后node基于它有了服务端模块概念化

js
//utils.js 文件
function add(a, b) {
  console.log(a + b);
}

module.exports.add = add;

//main.js 文件
var add = require("./utils").add;
add(1, 2);

CommonJS 出来后服务端模块化概念形成 CommonJS 读取数据是同步的需要读取服务端数据, 但浏览器也要读取数据,网络延迟,需要很长的时间,由于多个js存在先后依赖关系很难保证加载顺序, 并且造成浏览器假死的状态,然后出现 AMD

2. 2010年 AMD(Asynchronous Module Definition 异步模块定义)

2010年美国程序员James Burk开发了require.js

模块的加载不影响后面定义的运行,等 define 加载模块完成后,再执行 require 回调函数里面的就是依赖函数的输出 AMD 依赖 require.js(一个库)

多个文件可能会有依赖关系,依赖的文件可能要早于被依赖的文件,就可以通过 require.js 解决

js
//utils.js
define ([], function () {
    return {
        add: function(a, b) {
            console. log(a + b)
    }
    };
});
// main.js 文件
require(['./utils'], function (utils) {
    utils.add(1, 2)
}):

但是在使用 require.js 之前我们需要提前加载所需要的依赖

所以 玉伯在开发cjs的时候提出了 CMD规范

3. 2011年 CMD( Common Module Definition)

2011年 支付宝前端玉伯开发了seajs,提出了CMD

规范代表库:sea.js

SeaJS 是一个 JavaScript 模块加载框架,可以实现 JavaScript 的模块化开发及加载机制。 CMD 与 AMD 很类似,不同点在于:AMD 推崇依赖前置、提前执行,CMD 推崇依赖就近、延迟执行。此规范其实是在 sea.js 推广过程中产生的。

js
// AMD
require(["./utils", "a", "b"], function (utils) {
  console.log(1);
  //还没有用到 utils a,b 等模块,但是AMD已经初始化了所有模块
  console.log(2);
  utils.add(1, 2);
});

//CMD
define(function (require, exports, module) {
  console.log(1);
  if (false) {
    var utils = require("./utils"); //需要时再require,执行就不会加载
    utils.add(1, 2);
  }
});

4. UMD (Universal Module Definition) [ˌjuːnɪˈvɜːs(ə)l]

UMD 规范只是一种通用的写法,是在 amd 和 cjs 两个流行而不统一的规范情况下,才催生出 umd 来统一规范的,umd 前后端均可通用。

js
(function (root, factory) {
    if (typeof define ==- 'function' && define.amd) {
    //AMD
    define(['utils'], factory);
    ] else if (typeof exports ===
    'object') {
    //CommonJS
    var utils = require( 'utils');
    module.exports = factory (utils) ;
    } else {
        // 如果都不是就挂载到全局对象上
    root.result = factory(root.utils) ;
    }
}(this, function(utils) {
    utils.add(1, 2)
})

5. 2015年 ESM(es6)

esm 规范是 es6 原生支持的,很多浏览器开始支持,类似 commonjs 的写法和同、异步加载机制能通过设置 type=module,用于 html 中,而且在 node 中也开始支持啦! export 向外暴露或导出模块 export default xxx; import 引入暴露或导出的模块 import {xx, xx} from ‘./xxx.js’;

js
export const utils = {
  add: function (a, b) {
    console.log(a + b);
  },
};
// main.js 文件
import { utils } from "./utils";
utils.add(1, 2);

PS:注意事项

CommonJS 与 ES6 的区别

CommonJS 模块输出的是一个值的拷贝(浅拷贝),ES6m 模块输出的是值的引用

CommonJS 模块是运行时加载,ES6 模块是编译时输出接口

  • 因为 CommonJS 加载的是一个对象(即 module.exports 属性),该对象只有在脚本运行完成才会生成。而 ES6 模块不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成

CommonJS 模块是 Node.js 专用的(其实浏览器也能用),与 ES6 模块不兼容。而ES6模块化在浏览器和node.js中都可以用。

  • module, exports, require ,global,只需要提供这4个环境变量,然后browserify这个处理了这个4个环境变量,可以在浏览器上运行

  • 在Node中真正用于导出的其实根本不是exports,而是module.exports,module才是导出的真正实现者;

语法上面,两者最明显的差异是,CommonJS 模块使用require()和module.exports,ES6 模块使用import和export。

在node.js使用模块化,需要将 CommonJS 脚本的后缀名都改成.cjs,ES6 模块采用.mjs后缀文件名。或者修改package.son里面的文件,type字段为module或commonjs

模块化历史

requirejs昙花一现,大概在2016年之间的项目,现在几乎不会再使用,seajs也是一样

在nodejs中 可以同时使用commonjs和ES6模块化

在vue-cli构建的项目中,webpack采用commonJS规范,因为webpack基于node运行默认是支持commonJs的

在基于webpack开发的前端项目中,可以直接采用es6模块化规范,比如vue和react等项目,因为webpack集成了babel插件

TS 可以生成各种加载时的代码模块

现在主流都采用ESM

提问

  1. commonjs在浏览器能用吗?

    不能直接用,但可以提供 module, exports, require ,global环境变量,支持运行,browserify[ˈbraʊzə(r)ify]/ webapck 就是做这件事

  2. 那commonjs对浏览器的意义在哪里?

    初期前端模块化的探索产物,为es 发展下一个js语言版本奠定了基石,这就是现在的es6规范

    前期没有前端模块化的时候,vue和react是用 node rollup ast编译压缩去除无效代码 打包构建后的产物还是commonjs 但是用Browserify/webapck可以在浏览器运行

参考

https://github.com/buppt/Video-article-blog/issues/1