前端模块化-CJS、AMD、UMD、CMD、ESM规范

285 阅读4分钟

CommonJS(CJS)--同步加载

特点:

  • 封闭的独立作用域,暴露出去的是拷贝值,不会影响内部值;

  • 使用require导入模块

  • 使用module.exports或者exports[xxx]导出内容

优点:

率先在服务端实现了从框架层面解决依赖模块化、全局变量污染的问题

缺点:

是针对服务端的解决方案,对异步场景没有过多的处理和考虑;

  • CSJ是同步加载,对于Nodejs服务端加载时,由于本地资源不需要网络请求,同步加载在性能方面比较优势;
  • 浏览器环境通常需要网络请求来获取资源,如果同步加载的话,一旦网络环境不好,或者某个模块加载较慢,影响较大;

使用方式:

  • ①给 module.exports 赋值, 重复赋值,导出的对象会被覆盖
//exports.js
module.exports={
    a:1
}
module.exports={
    c:3
}

//script.js
var obj = require('./exprot');
console.log('log=>obj',obj);//log=>obj { c: 3 }
  • ②给 exports 添加属性和赋值,操作的都是exports对象
//exports.js
exports.b = 2;
exports.d = 4;

//script.js
var obj = require('./exprot');
console.log('log=>obj',obj);//log=>obj { b: 2, d: 4 }

  • ③同时使用 module.exportsexports (不建议)
    • module.exports 赋值会覆盖导出的内容
    • exports类似module.exports 的快捷方式,默认指向module.exports
    • module.exports原始值一般是一个空对象
//exports.js
module.exports={
    a:1
}
exports.b = 2;
module.exports={
    c:3
}
exports.d = 4;

//script.js
var obj = require('./exprot');
console.log('log=>obj',obj);//log=>obj { c: 3 }

AMD规范(异步模块定义)

特点

优点:

  • 相比CJS来说,AMD支持了异步模块加载,解决了浏览器异步加载模块的问题
  • 对于NodeJs这样的服务器端,AMD的模块载入无需阻塞服务器进程,同样提高了性能

缺点:

  • 由于依赖前置,没有按需加载,不论用或者不用,都提前加载执行,影响性能

使用方式:

资源目录:

-amd.html
-Require.js
-module -丨
          丨-moduleA.js
          丨-moduleB.js
amd.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<script src="Require.js"></script>
<script>
   require(['module/moduleA', 'module/moduleB'], 
        function(moduleA, moduleB) {
            console.log('moduleA.getMessage():',moduleA.getMessage());//moduleA.getMessage(): 这是module A的msg
            console.log('moduleB.getMessage:',moduleB.printMessage);//moduleB.getMessage:  ƒ () {//...} moduleB暴露的printMessage函数
            moduleB.printMessage() // 这是module A的msg 执行的是模块B中,加载的模块A的printMessage函数
        }
    );
</script>
<body>
    
</body>
</html>

module/moduleA.js
define(function() {
    var message = "这是module A的msg";
    return {
      getMessage: function() {
        return message;
      }
    }
  });
module/moduleB.js
define(['module/moduleA'], function(moduleA) {
    return {
      printMessage: function() {
        console.log(moduleA.getMessage());
      }
    }
  });

UMD规范(CJS + AMD)

特点

  • 由于 CJS 与 AMD 方式同时存在,导致有的 模块是 CJS 有的是 AMD 方式,对于三方引用时很不友好,于是就有了UMD
  • UMD = CJS + AMD;(使用IIFE[立即执行函数]实现的一种兼容规范)

优点:

  • 跟AMD一样,实现了异步加载

缺点

  • 融合了CJS和AMD,一样是依赖前置,不论用或者不用,都提前加载

实现方式:

(function (root, factory) {
    if (typeof define === 'function' && define.amd) {
        // AMD
        define(['jquery', 'underscore'], factory);
    } else if (typeof exports === 'object') {
        // Node, CommonJS-like
        module.exports = factory(require('jquery'), require('underscore'));
    } else {
        // Browser globals (root is window)
        root.returnExports = factory(root.jQuery, root._);
    }
}(this, function ($, _) {
    //    methods
    function a(){};    //    private because it's not returned (see below)
    function b(){};    //    public because it's returned
    function c(){};    //    public because it's returned

    //    exposed public methods
    return {
        b: b,
        c: c
    }
}));

CMD规范(通用模块定义)

特点

  • Sea.js的模块化规范,它是一种在浏览器端使用的模块加载器,需要引入sea.js资源
  • 基于cjs规范

优点:

  • 按需加载,就近加载,延迟执行,提升性能
    • define的回调函数中的require中按需加载(下面的module.js有demo)

缺点:

  • 依赖于打包,加载逻辑会打包到模块中,增加了模块体积

使用方式:

资源目录:

-app.js
-cmd.html
-module.js
-sea.js
amd.html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Sea.js Demo</title>
</head>
<body>
  <script src="./sea.js"></script>
  <script>
    seajs.use('./app.js');//5
    
    seajs.use(['./module.js'],function(add){
        console.log('log=>sum',add(1,2)); //3
    })
  </script>
</body>
</html>

app.js
define(function(require) {
    // 导入module.js模块
    var add = require('./module');
  
    // 调用add函数
    var result = add(2, 3);
  
    // 输出结果
    console.log(result); // 5
  });
  
module.js
define(function(require, exports, module) {
     // require('./xxx')   <---------在这里按需加载
    // 定义add函数
    function add(a, b) {
      return a + b;
    }
    
    //这里的导出与cjs是一样的
    //exports.min = function(a, b){ return a > b ? b : a; } 
  
    // 导出add函数,以便在其他模块中使用
    module.exports = add;
  });
  

ESM(es6的导入导出规范)

特点

  • ES6 在语言标准的层面上,实现了模块化,旨在成为浏览器和服务器通用的模块化方案,用 export 导出模块,import 导入模块。
  • importexport语句属于ES模块化语法,需要在支持ES模块的环境中使用,例如在Node.js或者使用工程化工具(如Webpack、Babel等)进行构建。

优点:

  • 规范了此前的不同标准。

使用方式:

导出多个声明
//export.js
export test = () => {}

//import.js
import { test } from './export.js'
导出单个内容
//export.js
export default { name:'tom' }

//import.js
import defaultExport from './export.js
混用
//export.js
export test = () => {}
export default { name:'tom' }

//import.js
import defaultExport,{test} from './export.js
//defaultExport只是别名