CJS、ESM、AMD、CMD、UMD

CJS

  • 规范代表库:CommonJS

CJSCommonJS 的缩写。

nodejs里的规范,环境变量:

  • module
  • exports
  • require
  • global

每一个文件是一个模块,有自己的作用域。在文件内定义的变量、函数、类都是私有的,对其他文件不可见。
global是全局变量,多个文件内可以共同分享变量。
commonjs规定:
每个模块内部,module变量代表当前模块,该变量是一个对象。他有一个exports属性,这个属性是对外的接口。加载某一个模块,其实就是加载该模块的module.exports属性。

经常我们这么使用:

// importing 
const doSomething = require('./doSomething.js'); 

// exporting
module.exports = function doSomething(n) {
  // do something
}
  • 所有代码运行在模块作用域内,不会污染全局变量
  • CJS 是同步导入模块
  • 模块可以加载多次,但是只有第一次加载时运行一次。然后运行结果就被缓存下来,以后再加载,就是直接读取缓存的结果。
  • CJS 不能在浏览器中工作。它必须经过转换和打包
Module对象

node内部提供一个Module构建函数。每一个模块内部都有一个module对象,代表当前模块。它有以下属性:

  • id 模块标识符,通常是带有绝对路径的模块文件名
  • path 模块的文件名,绝对路径
  • exports 模块对外输出的值
  • parent 调用该模块的模块
  • children 该模块用到的其他模块
  • loaded 该模块是否已经加载完(在父模块中require一个子模块之后,子模块的loaded才变为true)
module.exports

表示对外输出的接口,其他文件加载该模块其实就是读取module.exports变量

exports

为了方便,Node为每一个模块提供了exports变量,指向module.exports。等同于

var exports = module.exports

所以切记,不可以直接给exports赋值,这样就是切断了 exports和module.exports的联系。

exports = () => {}

只可以在exports上添加属性。 如果该模块对外输出的是一个简单类型的值。那么不可以用exports去输出了,只能用module.exports = ‘xx’

加载模式 - 同步/运行时加载

首先,commonjs加载模块的方式是同步的。在输入时先加载整个模块,生成一个对象。再从这个对象上读取方法,这种加载被成为运行时加载。只有对应子模块加载完成,才能执行后面的操作。 为什么是同步的?因为Nodejs是用于服务端编程,模块文件存在于硬盘中,读取非常快。

加载时机

输入的值是被输出的值的拷贝。 父模块引入了一个子模块,其实引入的是这个子模块输出的值的拷贝,一旦输出了这个值,模块内部的变化就影响不到这个值了。

loadsh 是CJS模块化规范的发布包

CJS、ESM、AMD、CMD、UMD_第1张图片

AMD

  • 规范代表库:require.js

AMD 代表异步模块定义,浏览器环境要加载资源,需要从服务器端加载模块,依靠网络下载,时间比较长。所以需要采用非同步的模块。下面是一个示例代码

define(['dep1', 'dep2'], function (dep1, dep2) {
    //Define the module value by returning a value.
    return function () {};
});

或者

// "simplified CommonJS wrapping" https://requirejs.org/docs/whyamd.html
define(function (require) {
    var dep1 = require('dep1'),
        dep2 = require('dep2');
    return function () {};
});
  • AMD 是异步(asynchronously)导入模块的(因此得名)
  • 一开始被提议的时候,AMD 是为前端而做的(而 CJS 是后端)
  • AMD 的语法不如 CJS 直观。我认为 AMDCJS 完全相反

UMD

UMD 代表通用模块定义(Universal Module Definition),是一种思想,兼容commonjs、AMD、CMD。

先判断是否支持Nodejs模块(exports是否存在),如果存在就使用Nodejs模块。不支持的话,再判断是否支持AMD/CMD(判断define是否存在),都不行就挂载在window全局对象上

下面是它可能的样子(来源)

(function (root, factory) {
    if (typeof define === "function" && define.amd) {
        define(["jquery", "underscore"], factory);
    } else if (typeof exports === "object") {
        module.exports = factory(require("jquery"), require("underscore"));
    } else {
        root.Requester = factory(root.$, root._);
    }
}(this, function ($, _) {
    // this is where I defined my module implementation

    var Requester = { // ... };

    return Requester;
}));
  • 在前端和后端都适用(“通用”因此得名)
  • CJSAMD 不同,UMD 更像是一种配置多个模块系统的模式。这里可以找到更多的模式
  • 当使用 Rollup/Webpack 之类的打包器时,UMD 通常用作备用模块
js-cookie 是UMD模块化规范的发布包

CJS、ESM、AMD、CMD、UMD_第2张图片

ESM

  • esm规范是es6原生支持的,很多浏览器开始支持,类似commonjs的写法和同、异步加载机制

ESM 代表 ES 模块。这是 Javascript 提出的实现一个标准模块系统的方案,其模块功能由两个命令组成:export 和 import。

我相信你们很多人都看到过这个:

import React from 'react';

或者其他更多的

import {foo, bar} from './myLib';

...

export default function() {
  // your Function
};
export const function1() {...};
export const function2() {...};

ES6模块的特征:

  • import 是只读属性,不能赋值。相当于const
  • 它兼具两方面的优点:具有 CJS 的简单语法和 AMD 的异步
  • 得益于 ES6 的静态模块结构,可以进行 Tree Shaking
  • ESM 允许像 Rollup 这样的打包器,删除不必要的代码,减少代码包可以获得更快的加载
  • export/import 提升,import/export必须位于模块的顶级,不可以位于作用域内,其次对于模块内的import/export都会提升到模块的顶部。
  • 可以在 HTML 中调用,只要如下
<script type="module">
  import {func1} from 'my-lib';

  func1();
</script>

但是不是所有的浏览器都支持(来源)。

ES6 Module 加载时机

import 是静态命令的方式,js引擎对脚本静态分析时,遇到模块加载命令import,会生成一个只读引用。等到脚本真正执行时,再根据这个只读引用,到被记载的那么模块中去取值。模块内部引用的变化会反应在外部。

在import时可以指定加载某个输出值,而不是加载整个模块,这种加载称为编译时加载。在编译时就引入模块代码,而不是在代码运行时加载,所以无法实现条件加载。也正因为这个,使得静态分析成为可能。

loadsh-es 是ESM模块化规范的发布包

CJS、ESM、AMD、CMD、UMD_第3张图片

CMD

  • 规范代表库:sea.js

sea.js 都是异步加载, AMD推崇的是前置依赖,提前执行。CMD推崇的是就近依赖,延迟执行

你可能感兴趣的:(javascript,前端,npm)