[TOC]

模块化是一种处理复杂系统分解为更好的可管理模块的方式。简单来说就是解耦,简化开发,一个模块就是实现特定功能的文件,可以更方便地使用别人的代码,想要什么功能,就加载什么模块。模块开发需要遵循一定的规范。
常见规范有

  • CommonJs
  • AMD
  • UMD
  • CMD
  • ES6的Module

    CommonJS规范

    用在服务器端的node的模块规范,前端的webpack也是对CommonJS原生支持的。

    特点:
  • 模块输出的是一个值的拷贝, 模块是运行时加载,同步加载

  • CommonJS 模块的顶层this指向当前模块

    有两个API :
  • require : 加载所要依赖的其他模块

  • module.exports 或者 exports : 对外暴露的接口

    注意
  • exports 与module.exports 的区别:exports 是对 module.exports 的引用,不能直接给exports 赋值,直接赋值无效,结果是一个空对象,module.exports 可以直接赋值

  • . 一个文件不能写多个module.exports ,如果写多个,对外暴露的接口是最后一个module.exports
  • . 模块如果没有指定使用module.exports 或者exports 对外暴露接口时,在其他文件就引用该模块,得到的是一个空对象{}

    AMD

    AMD 即 Asynchronous Module Definition,中文名是“异步模块定义”的意思。它是一个在浏览器端模块化开发的规范,AMD 是 RequireJS 在推广过程中对模块定义的规范化产出,所以AMD规范的实现,就是的require.js了

    特点
  • 异步加载,不阻塞页面的加载,能并行加载多个模块,但是不能按需加载,必须提前加载所需依赖

    两个重要的api
  • define(id?,[]?,callbakc)

  • require([module], callback)

还有一个配置属性API:

  • require.config({

baseUrl, paths, shim
})

注意
  • paths 的设置加载优化与shim 中配置(AMD模式优先非AMD模式)
  • 加载非AMD 模式的模块,依赖非AMD模块 ,第三方插件(如jquery .lazyload 插件)这里用到了shim属性配置(加载非AMD 模式)

    UMD

    其实是amd和commonjs的统一规范,支持两种规范,即写一套代码,可用于多种场景。
    毋庸置疑的是,其写法也是最复杂的

  • 前后端均通用

  • 与CJS或AMD不同,UMD更像是一种配置多个模块系统的模式。
  • UMD在使用诸如Rollup/ Webpack之类的bundler时通常用作备用模块

    (function (root, factory) {
    if (typeof define === 'function' && define.amd) {
      // AMD
      define(['jquery'], factory);
    } else if (typeof exports === 'object') {
      // Node, CommonJS之类的
      module.exports = factory(require('jquery'));
    } else {
      // 浏览器全局变量(root 即 window)
      root.returnExports = factory(root.jQuery);
    }
    }(this, function ($) {
    //    方法
    function myFunc(){};
    
    //    暴露公共方法
    return myFunc;
    }));
    

    CMD

    CMD规范是阿里的玉伯提出来的,实现js库为sea.js。 它和requirejs非常类似,即一个js文件就是一个模块,但是CMD的加载方式更加优秀,是通过按需加载的方式,而不是必须在模块开始就加载所有的依赖。

    ES6的Module (ESM)

    ES6 在语言标准的层面上,实现了模块功能,而且非常简单,ES6到来,完全可以取代 CommonJS 和 AMD规范,成为浏览器和服务器通用的模块解决方案。由vue,Angular React这些mvvm 模式的框架发展,让前端的编程变得模块化,组件化。

    特点
  • ES6 模块之中,顶层的this指向undefined,即不应该在顶层代码使用this。

  • 自动采用严格模式”use strict”。须遵循严格模式的要求
  • ES6 模块的设计思想是尽量的静态化,编译时加载”或者静态加载,编译时输出接口
  • ES6 模块export、import命令可以出现在模块的任何位置,但是必须处于模块顶层。如果处于块级作用域内,就会报错
  • ES6 模块输出的是值的引用
  • 很多浏览器开始支持
  • 拥有类似commonjs的写法和同、异步加载机制
  • 能通过设置type=module,用于html中
  • node也开始支持

    两个主要API
  • export:用于规定模块的对外接口,

  • import:用于输入其他模块提供的功能。
    注意
  1. export命令除了输出变量,还可以输出函数或类(class),不同的数据类型
  2. 使用export命令定义了模块的对外接口以后,其他 JS 文件就可以通过import命令加载这个模块。
  3. 本质上,export default就是输出一个叫做default的变量或方法 ```javascript // modules.js function add(x, y) { return x * y; } export {add as default}; // 等同于 // export default add;

// app.js import { default as foo } from ‘modules’; // 等同于 // import foo from ‘modules’;

```javascript
// modules.js
function add(x, y) {
  return x * y;
}
export {add as default};
// 等同于
// export default add;

// app.js
import { default as foo } from 'modules';
// 等同于
// import foo from 'modules';

CommonJS vs ES6 Module:

  • CommonJS支持动态导入,也就是 require(${path}/xx.js),ES6目前不支持
  • CommonJS是同步导入,因为用于服务端,文件都在本地,同步导入即使卡住主线程影响也不大。而ES6是异步导入,因为用于浏览器,需要下载文件,如果也采用同步导入会对渲染有很大影响。
  • ES6模块中的值属于【动态只读引用】。只读:import的变量是只读的,不论是基本数据类型还是复杂数据类型,在不能在导入的文件修改,复杂数据类型可以修改属性值不会报错,但是不建议这么做。动态:原始值发生变化,import加载的值也会发生变化。不论是基本数据类型还是复杂数据类型。
    CommonJS导出时都是值拷贝,就算导出的值变了,导入的值也不会改变,当导出的是复杂数据类型时,属于浅拷贝,由于引用相同,所以修改会互相影响。使用require命令加载某个模块时,就会运行整个模块的代码。当再次使用require命令加载同一个模块时,不会再执行该模块,而是取到缓存之中的值,除非手动清除系统缓存。
  • ES6编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonJS 和 AMD 模块,在运行时才能确定。

浏览器加载规则:
1、浏览器加载 ES6 模块,也使用