模块化-CommonJs、AMD、CMD、ES6

在了解AMD,CMD规范前,还是需要先来简单地了解下什么是模块化,模块化开发?模块化是指在解决某一个复杂问题或者一系列的杂糅问题时,依照一种分类的思维把问题进行系统性的分解以之处理。模块化是一种处理复杂系统分解为代码结构更合理,可维护性更高的可管理的模块的方式。可以想象一个巨大的系统代码,被整合优化分割成逻辑性很强的模块时,对于软件是一种何等意义的存在。对于软件行业来说:解耦软件系统的复杂性,使得不管多么大的系统,也可以将管理,开发,维护变得“有理可循”。

模块化理解

1. 什么是模块

  • 将复杂的程序依据一定的规则(规范)拆分成多个模块(文件)
  • 模块的内部数据与实现是私有的, 只是向外部暴露一些接口(方法)与外部其它模块通信

2. 模块化的进化过程

  • 全局function : 将不同的功能封装成不同的全局函数

    缺点:虽说可以实现一定的封装效果,但是大量的全局函数,污染全局命名空间,容易引起命名冲突
function module1 () {
 //...
}
function module2 () {
 //...
}
  • 命名空间 : 简单对象封装

    缺点:减少了全局变量,解决命名冲突,但是外部可以直接修改模块内部的数据
let module = {
  data: 'aaa',
  func () {
    console.log(`${this.data}`)
  }
}
module.data = 'bbb' // 直接修改模块内部的数据
module.fn() // bbb
  • IIFE:(自执行函数)

    缺点:实现数据私有, 外部只能通过暴露的方法操作,如果当前这个模块依赖另一个模块怎么办?
  // module.js文件()
(function (window) {
  let data = 'aaa'
  function func () {
    console.log(`${this.data}`)
  }
  //暴露接口
  window.module = { func }
})(window)
// index.html文件


  • IIFE增强 : 引入依赖

  // module.js文件
(function (window, $) {
  let data = 'aaa'
  function func () {
    console.log(`${this.data}`)
  }
  function func2 () {
    $('body').css('background', 'red')
  }
  //暴露接口
  window.module = { func, func2 }
})(window, jQuery)
 // index.html文件
  
  
  
  

上面引入jQuery库,就把这个库当作参数传入,保证模块的独立性,使得模块之间的依赖关系变得明显。

3. 模块化的作用

通过上面的模块拆分,我们发现:

  • 减少了全局变量,有效的避免了命名污染
  • 更好的分离,按需加载
  • 提高了复用性,维护性

但是比较复杂的应用,模块比较多,难免需要引入多个

image

require()函数在加载依赖的函数的时候是异步加载的,这也是我在这里放了个setTimeout证实一下,这样浏览器不会失去响应,它指定的回调函数,只有前面的模块都加载成功后,才会运行,解决了依赖性的问题。AMD的异步加载解决了阻塞加载、性能问题,模块之间的依赖关系也能清楚的显示出来。

  • CMD (Common Module Definition)

CMD 是 SeaJS 在推广过程中对模块定义的规范化产出。CMD规范和 AMD 很相似,解决同样问题,只是运行机制不同。对于依赖的模块,CMD 推崇延迟执行(依赖就近)

// a.js(定义没有依赖的模块)
define(function (require, exports, module) {
    let data = 'aaa'
    function doSomething () {
        console.log(data)
    }
    exports.doSomething = doSomething // 暴露接口
})
// b.js (定义有依赖的模块)
define(function (require, exports, module) {
    let data = 'bbb'
    function doSomething () {
        var c = require('./c') // 依赖可以就近书写  
        console.log(data + c.data)
    }
    exports.doSomething = doSomething // 暴露接口
})
// c.js (此模块为 b.js 依赖)
define(function (require, exports, module) {
    let data = 'ccc'
    exports.data = data // 暴露模块
})
// 引入依赖的模块
define(function (require, exports, module) {
  //引入依赖模块(异步)
  require.async('./a', function (a) {
    a.doSomething()
    console.log('a是异步的')
  })
  //引入依赖模块(同步)
  var b = require('./b') // 依赖可以就近书写  
  b.doSomething()
  // ... 
  var c = require('./c') // 依赖可以就近书写  
  console.log(c.data)
  // ...
})

    
    

image
  • ES6

ES6 模块的设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonJS 和 AMD 模块,都只能在运行时确定这些东西。

export命令用于规定模块的对外接口,import命令用于输入其他模块提供的功能。为了提供方便,不用阅读文档就能加载模块,就要用到export default命令,为模块指定默认输出。

// a.js (定义模块)
var data = 'aaa'
var doSomething = function () {
  console.log('log: ' + data)
};
export { data, doSomething }

// 引用模块 
import { data, doSomething } from './a'

这里在语法不做过多介绍,主要说一说 ES6 模块CommonJS 模块 的差异。

它们有两个重大差异:

  • CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。
  • CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。

第二个差异是因为 CommonJS 加载的是一个对象(即module.exports属性),该对象只有在脚本运行完才会生成。而 ES6 模块不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成。

我们来看看第一个差异,CommonJS模块的加载机制:

// module1.js
var data = 5;
var doSomething = function () {
  data++;
};
// 暴露的接口
module.exports.data = data;
module.exports.doSomething = doSomething;
var example = require('./module1.js');
console.log(example.data); // 5
example.doSomething(); 
console.log(example.data); // 5

ES6 模块的加载机制:

// module1.js
let data = 5;
function doSomething() {
  data++;
}
export { data, doSomething }
import { data, doSomething } from './module1';
console.log(data); // 5
doSomething();
console.log(data); // 6

ES6 模块的运行机制与 CommonJS 不一样。ES6 模块是动态引用,并且不会缓存值,模块里面的变量绑定其所在的模块。

总结

  • CommonJS 模块输出的是一个值的拷贝,CommonJS 模块是运行时加载,CommonJS规范主要用于服务端编程,加载模块是同步的,同步意味着阻塞加载,浏览器资源是异步加载的,因此有了AMD、CMD解决方案。
  • AMD 是 RequireJS 在推广过程中对模块定义的规范化产出。AMD规范在浏览器环境中异步加载模块,而且可以并行加载多个模块。AMD 的 API 默认是一个当多个用,对于依赖的模块,AMD 推崇提前执行(依赖前置)
  • CMD 是 SeaJS 在推广过程中对模块定义的规范化产出。CMD 的 API 严格区分,推崇职责单一加载模块是异步的,CMD 推崇延迟执行(依赖就近)。
  • ES6 模块输出的是值的引用,ES6 模块是编译时输出接口,ES6 在语言标准的层面上,实现了模块功能简单,完全可以成为浏览器和服务器通用的模块解决方案。

你可能感兴趣的:(模块化-CommonJs、AMD、CMD、ES6)