前端基础知识:AMD、CMD 和 CommonJS 的模块化开发

AMD、CMD 和 CommonJS 的模块化开发

AMD/CMD/CommonJs都是JS模块化开发的标准,目前对应的实现是RequireJS,SeaJs, nodeJs;

CommonJS:服务端js

CommonJS 是以在浏览器环境之外构建 javaScript 生态系统为目标而产生的写一套规范,主要是为了解决 javaScript 的作用域问题而定义的模块形式,可以使每个模块它自身的命名空间中执行。

实现方法:模块必须通过 module.exports 导出对外的变量或者接口,通过 require() 来导入其他模块的输出到当前模块的作用域中;

主要针对服务端(同步加载文件)和桌面环境中,node.js 遵循的是 CommonJS 的规范;
CommonJS 加载模块是同步的,所以只有加载完成才能执行后面的操作.

**require()用来引入外部模块;
exports对象用于导出当前模块的方法或变量,唯一的导出口;
module对象就代表模块本身。**

// 定义一个module.js文件
var A = () => console.log('我是定义的模块');

// 1.第一种返回方式
module.exports = A; 
// 2.第二种返回方式 
module.exports.test = A
// 3.第三种返回方式 
exports.test = A;


// 定义一个test.js文件【这两个文件在同一个目录下】
var module = require("./module");

//调用这个模块,不同的返回方式用不同的方式调用
// 1.第一种调用方式
module();
// 2.第二种调用方式 
module.test();
// 3.第三种调用方式 
module.test();


// 执行文件
node test.js

AMD: 异步模块定义【浏览器端js】

AMD 是 Asynchronous Module Definition 的缩写,意思是异步模块定义;采用的是异步的方式进行模块的加载,在加载模块的时候不影响后边语句的运行。主要是为前端 js 的表现指定的一套规范。

实现方法:通过define方法去定义模块,通过require方法去加载模块。

define(id?,dependencies?,factory): 它要在声明模块的时候制定所有的依赖(dep),并且还要当做形参传到factory中。没什么依赖,就定义简单的模块(或者叫独立的模块)

require([modules], callback):第一个参数[modules],是需加载的模块名数组;第二个参数callback,是模块加载成功之后的回调函数

主要针对浏览器js,requireJs遵循的是 AMD 的规范;

// module1.js文件, 定义独立的模块
define({
    methodA: () => console.log('我是module1的methodA');
    methodB: () => console.log('我是module1的methodB');
});

// module2.js文件, 另一种定义独立模块的方式
define(() => {
    return {
        methodA: () => console.log('我是module2的methodA');
        methodB: () => console.log('我是module2的methodB');
    };
});

// module3.js文件, 定义非独立的模块(这个模块依赖其他模块)
define(['module1', 'module2'], (m1, m2) => {
    return {
        methodC: () => {
            m1.methodA();
            m2.methodB();
        }
    };
});


//定义一个main.js,去加载这些个模块
require(['module3'], (m3) => {
    m3.methodC();
});


// 为避免造成网页失去响应,解决办法有两个,一个是把它放在网页底部加载,另一个是写成下面这样:
<script src="js/require.js" defer async="true" ></script>
// async属性表明这个文件需要异步加载,避免网页失去响应。
// IE不支持这个属性,只支持defer,所以把defer也写上。

// data-main属性: 指定网页程序的主模块
<script data-main="main" src="js/require.js"></script>

// 控制台输出结果
我是module1的methodA
我是module2的methodB

CMD: 通用模块定义【浏览器端js】

CMD 是 Common Module Definition 的缩写,通过异步的方式进行模块的加载的,在加载的时候会把模块变为字符串解析一遍才知道依赖了哪个模块;

主要针对浏览器端(异步加载文件),按需加载文件。对应的实现是seajs

AMD和CMD的区别:

1. 对于依赖的模块,AMD 是提前执行,CMD 是延迟执行。不过 RequireJS 从 2.0 开始,也改成可以延迟执行(根据写法不同,处理方式不同)。CMD 推崇 as lazy as possible(尽可能的懒加载,也称为延迟加载,即在需要的时候才加载)。

2. CMD 推崇依赖就近,AMD 推崇依赖前置。

// CMD
define(function(require, exports, module) {
    var a = require('./a');
    a.doSomething();
    // ...
    var b = require('./b');   // 依赖可以就近书写
    b.doSomething();
    // ... 
})

// AMD
define(['./a', './b'], function(a, b) { // 依赖必须一开始就写好
    a.doSomething();
    // ...
    b.doSomething();
    //...
}) 

import和require

import和require都是被模块化使用

  • require是CommonJs的语法(AMD规范引入方式),CommonJs的模块是对象。
  • import是es6的一个语法标准(浏览器不支持,本质是使用node中的babel将es6转码为es5再执行,import会被转码为require),es6模块不是对象
  • require是运行时加载整个模块(即模块中所有方法),生成一个对象,再从对象上读取它的方法(只有运行时才能得到这个对象,不能在编译时做到静态化),理论上可以用在代码的任何地方。
  • import是编译时调用,确定模块的依赖关系,输入变量(es6模块不是对象,而是通过export命令指定输出代码,再通过import输入,只加载import中导的方法,其他方法不加载),import具有提升效果,会提升到模块的头部(编译时执行)

​ export和import可以位于模块中的任何位置,但是必须是在模块顶层,如果在其他作用域内,会报错
​ es6这样的设计可以提高编译器效率,但没法实现运行时加载

  • require是赋值过程,把require的结果(对象,数字,函数等),默认是export的一个对象,赋给某个变量(复制或浅拷贝)
  • import是解构过程(需要谁,加载谁)
// require/exports(仅有下面的三种简单写法)

const a = require('a') //真正被require出来的是来自module.exports指向的内存块内容

exports.a = a //exports 只是 module.exports的引用,辅助module.exports操作内存中的数据
module.exports = a

// import/export
import a from 'a'
import { default as a  } from 'a'
import  *  as a  from 'a'
import { fun1,fun2 } from 'a'

export default a
export const a=1
export functon a{ }
export { fun1,fun2 }

Require.js

require.js的诞生,就是为了解决这两个问题:

(1)实现js文件的异步加载,避免网页失去响应;

(2)管理模块之间的依赖性,便于代码的编写和维护。

加载js文件

加载require.js,也可能造成网页失去响应。解决办法有两个,一个是把它放在网页底部加载,另一个是写成下面这样:

<script src="js/require.js" defer async="true" ></script>

async属性表明这个文件需要异步加载,避免网页失去响应。

IE不支持async,只支持defer,所以把defer也写上。

require.config()

在用require()加载之前,要先用require.config()方法,定义它们的一些特征

require.config({
  baseUrl: "js/lib",
  paths: {
    "jquery": "jquery.min",
    "underscore": "underscore.min",
    "backbone": "backbone.min"
  },
  shim: {
    'underscore':{
      exports: '_'
    },
    'backbone': {
      deps: ['underscore', 'jquery'],
      exports: 'Backbone'
    }
  }
});
THE END