js模块规范:AMD,CMD,CommonJS

js模块规范:CommonJS、AMD、CMD、ES6

一、模块化的理解

1、什么是模块

将一个复杂的程序依据一定的规范封装成几个文件,并进行组合在一起

2、为什么要模块化?
  • 降低复杂度
  • 提高解耦性
  • 部署方便
3、模块化的优点
  • 避免命名冲突(减少命名空间污染),减少全局变量污染
  • 提高了可复用性
  • 代码更易维护
  • 模块分离可以实现按需加载
  • 一定程度上减少了http请求的数量

二、CommonJS

CommonJS是同步加载,NodeJS就采用了CommonJS,因为在服务器读取模块都是在本地磁盘,加载速度很快,适合服务端。CommonJS规范规定,每一个文件就是一个模块,拥有自己独立的作用域,变量,以及方法等,对其他的模块都不可见

//加载模块
var modA= require('modA');
modA.start();

三、AMD (Asynchronous Module Definition)

AMD 是异步加载,requireJs应用了这一规范,先定义所有依赖,然后在加载完成后的回调函数中执行。

1、使用define来定义模块

define(id?,dependencies?,factory)。id为字符串类型唯一用来标识模块(可以省略),dependencies是一个数组字面量,用来表示当前定义的模块所依赖的模块(默认后缀名是.js),当没有dependencies时,表示该模块是一个独立模块,不依赖任何模块。factory是一个需要实例化的函数,函数的参数与依赖的模块一一对应,函数需要有返回值,函数的返回值表示当前模块暴露的内容。

2、使用require来调用模块

require函数的定义是require(dependencies,factory)。dependencies是一个数组字面量,表示调用的模块,factory需要传入一个回调函数,用来说明模块异步加载完成后执行的操作
require函数本身也是一个对象,它带有一个config函数用来配置require函数的运行参数,config函数接受一个对象作为参数。config的参数对象有以下几个属性:

  • baseUrl
    baseUrl参数指定本地模块位置的基准目录,即本地模块的路径是相对于哪个目录的。
  • paths
    paths参数指定各个模块的位置。这个位置可以是服务器上的相对位置,也可以是外部源。可以为每个模块定义多个位置,如果第一个位置加载失败,则加载第二个位置,后面我们将看到具体例子。
  • shim
    有些库不是AMD兼容的,这时就需要指定shim属性的值。shim是用来帮助require.js加载非AMD规范的库。
3、引用示例
// module1.js
// 定义一个没有依赖的模块
define(function() {
  let msg = '123456';
  function get() {
    return msg;
  }
  return { get }; // 暴露模块
})

// module2.js
// 定义一个依赖module1的模块
define(['module1'],function(module1) {
  let _msg_ = '7890';
  function con() {
    return module1.get()+_msg_;// 将两个字符串连接起来
  }
  return { con }; // 暴露模块
})

// main.js
function(){
  require.config({
    baseUrl:'./modules/',
    paths:{
      module1:'module1',
      module2:'module2'
    },
  });
  require([module2],function(module2){
    console.log(module2.con());
  });
}();// 1234567890

// index.html
<!DOCTYPE html>
<html>
  <head>
    <title>AMD</title>
  </head>
  <body>
    <!-- 引入require.js并指定js主文件的入口 -->
    <script data-main="js/main" src="js/libs/require.js"></script>
  </body>
</html>

在module.js文件中引入第三方库,main.js文件中也要有相应的路径配置。此外,我们还可以给一个库指定多个地址以作备用地址,在前面的地址失效的时候使用备用地址,例

// main.js
function(){
  require.config({
    baseUrl:'./modules/',
    paths:{
      jquery:'xxxx'
    },
  });
}();

require.config({
    paths: {
        jquery: [
        // 路径定义为数组,数组的每一项为一个地址定义
            '//cdnjs.cloudflare.com/ajax/libs/jquery/2.0.0/jquery.min.js',
            'lib/jquery'
        ]
    }
});

三、CMD (Common Module Definition)

整合了CommonJS和AMD规范的特点,专门用于浏览器端,异步加载模块,该规范明确了模块的书写格式和基本交互规则。
CMD规范通过define关键字来定义模块,基本语法为define(factory),factory的取值可以是函数或者任何合法的值(对象、数组、字符串等)。当factory是函数时,表示是该模块的构造函数,这个函数具有三个参数————“require、exports、module”。require参数是一个方法,它接受模块唯一标识作为参数,用来引入依赖。exports参数用来暴露模块,module参数指向当前模块。

异步的加载模块,seajs推崇的规范,CMD则是依赖就近,用的时候再require

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>CMD Demo</title>
    <!--引入Sea.js库-->
    <script type="text/javascript" src="../../sea-modules/seajs/seajs/2.2.0/sea.js"></script>
    <script type="text/javascript">
      seajs.use("../../static/cmd/main");// 使用模块
    </script>
  </head>
</html>

// module1.js
// factory为对象
define({foo:foo});

// module2.js
// factory为字符串
define(foo:"123456");

// module3.js
// factory为数字
define(foo:666);

上面是factory为普通值时候的例子,下面我们来看一个当factory为函数的时

// main.js
define(function(require) {
  //通过riquire引用模块
  var module1=require('./module1');
  var module2=require('./module2');
  var module3=require('./module3');
  console.log(module1.foo);// foo
  console.log(module2.foo);// 123456
  console.log(module3.foo);// 666
});

判断当前页面是否有CMD模块加载器

if(tepyof define === "function" && define.cmd){
  // 有Sea.js等CMD模块加载器存在
}

AMD和CMD最大的区别是对依赖模块的执行时机处理不同,而不是加载的时机或者方式不同,二者皆为异步加载模块。
AMD依赖前置,js可以方便知道依赖模块是谁,立即加载;而CMD就近依赖,需要使用把模块变为字符串解析一遍才知道依赖了那些模块

四、ES6

ES6模块规范的设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系。而CommonJS和CMD,都只能在运行时确定依赖。

  • 暴露接口
    export命令用于规定模块的对外接口,基本语法为export xxx,暴露的接口可以是对象、函数、基本类型变量。另外可以使用export default xxx为模块指定默认输出,因为很多时候用户不知道要加载的模块的属性名
  • 调用模块
    import命令用于输入其他模块提供的功能,基本语法为import xxx from xxx,其中第一个xxx为引入的模块的属性名,第二个xxx为模块的位置。
// module1.js
let s='';
function proc(){
  s='123456';
  return s;
}
export {num,inc};// 暴露接口

// main.js
import {s,proc} from './module1';// 引入依赖
console.log(s);// 
console.log(proc);// 123456

在使用CommonJS规范时,输出的是值的拷贝,也就是说输出之后,模块内部的变化不会影响输出。但在ES6中是恰好相反的,ES6规范中输出的是值的引用,也就是说模块内部变化会影响输出。
ES6模块是动态引用,不会缓存值,模块里面的变量绑定其所在的模块。

发布了27 篇原创文章 · 获赞 21 · 访问量 4614

猜你喜欢

转载自blog.csdn.net/weixin_43997143/article/details/100356407