JavaScript 的模块规范说明

Js协议和规范

例如下边的协议

  • CJS(Commonjs)
  • AMD(Asynchronous Module Definition)
  • ESM
  • UMD

CommonJS

最先大规模使用在nodejs上,是同步加载模块,主要用于服务端,也有在客户端使用cjs同步导入(使用),在网络环境差的情况下容易导致程序假死ADM规范便应用而生

  1. 导出
// module.js
module.exports = {  
    name: 'lindaidai',  
    sex: 'boy'  
} 
// module.js
exports.name = 'lindaidai';  
exports.sex = 'boy'  

我理解为导出的是modeule.exports 其中module意味着文件本身,exports是要导出的对象

  1. 导入
// 默认导入的就是js文件后缀名字
const module = require('./module');

AMD

AMD(异步模块定义)是为浏览器环境设计的,因为 CommonJS 模块系统是同步加载的,当前浏览器环境还没有准备好同步加载模块的条件。

define([module-name?],[array-of-dependencies?],[module-factory-or-object]);
  • module-name:模块标识,可以省略
  • array-of-dependencies:所依赖的模块数组,可以省略
  • module-factory-or-object:模块的实现或者一个 JavaScript 对象
  1. 导出
define(['package/lib'], function(lib){
  function foo(){
    lib.log('hello world!');
  }
  // 导出的回调
  return {
    foo: foo
  };
});

上方的模块意味这要导出一个含有依赖package/lib模块方法的一个模块,换句话说就是一个匿名模块依赖package/lib模块

//在模块定义内部引用依赖:
define(function(require) {
    var lib = require('package/lib');
    function foo(){
        lib.log('hello world!');
    }
    return {
        foo
    }
});
  1. 导入
require([module], callback);

第一个参数[module],是一个数组,里面的成员就是要加载的模块;第二个参数callback,则是加载成功之后的回调函数。类似于Promise.all回调的参数个数是根据前边的请求模块个数决定的

AMD和Commonjs导出规范略有差异,但是可以共存,这也为UMD的模块所能实现的基础,最下方会有demo代码

(function(golbal, factory){
    // AMD
    if(typeof define === "function" && define.amd)
        define(factory);
    // CommonJS
    else if(typeof require === "function" && typeof module === "object" && module && module.exports)
        module.exports = factory();
})(this, function(){
    // 定义类
    function Utils(name) {
        this._name = name;
    }
    // 定义类方法
    Utils.prototype.sayHi = function() {
        console.log("Hi, I am " + this._name);
    };
    // 定义静态方法
    Utils.add = function(a, b) {
        return a + b;
    }
    // 将类 Utils 作为当前文件的模块返回
    return Utils;
});

ESM(ES6 Modules)

ECMA Script Modules,是在 ES6 语言层面提出的一种模块化标准。这个DDDD // 常见的导入导出:

// 导入
import Vue from 'vue'
import VueRouter from 'vue-router'

// 导出
export const name = 'gsy'
export default {
    name: 'gsy'
}

UMD(Universal Module Definition)

Universal Module Definition,同时兼容 CJS 和 AMD,并且支持直接在前端用 <script src="lib.umd.js"></script> 的方式加载。现在还在广泛使用,不过可以想象 ESM 和 IIFE 逐渐代替它。

(function(root, factory) {
    if (typeof module === 'object' && typeof module.exports === 'object') {
        console.log('是commonjs模块规范,nodejs环境')
        var depModule = require('./umd-module-depended')
        module.exports = factory(depModule);
    } else if (typeof define === 'function' && define.amd) {
        console.log('是AMD模块规范,如require.js')
        define(['depModule'], factory)
    } else if (typeof define === 'function' && define.cmd) {
        console.log('是CMD模块规范,如sea.js')
        define(function(require, exports, module) {
            var depModule = require('depModule')
            module.exports = factory(depModule)
        })
    } else {
        // iife
        console.log('没有模块环境,直接挂载在全局对象上')
        root.umdModule = factory(root.depModule);
    }
}(this, function(depModule) {
    console.log('我调用了依赖模块', depModule)
	// ...省略了一些代码,去代码仓库看吧
    return {
        name: '我自己是一个umd模块'
    }
}))

IIFE(Immediately Invoked Function Expression)

Immediately Invoked Function Expression(立即执行函数),只是一种写法,可以隐藏一些局部变量,前端人要是不懂这个可能学的是假前端。可以用来代替 UMD 作为纯粹给前端使用的写法。

npm使用模块的优先级

由于我们使用的模块规范有 ESM 和 commonJS 两种,为了能在 node 环境下原生执行 ESM 规范的脚本文件,.mjs 文件就应运而生。

当存在 index.mjsindex.js 这种同名不同后缀的文件时,import './index' 或者 require('./index') 是会优先加载 index.mjs 文件的。

也就是说,优先级 mjs > js

mjs 是 EcmaScript 模块的扩展 package.json文件的字段区别

  • main : 定义了 npm 包的入口文件,browser 环境和 node 环境均可使用
  • module : 定义 npm 包的 ESM 规范的入口文件,browser 环境和 node 环境均可使用
  • browser : 定义 npm 包在 browser 环境下的入口文件
Last Updated: