模块化开发-模块化规范的出现

161 阅读5分钟

在模块化规范出现之前,模块化的实现方式都是以原始的模块系统为基础,并通过约定的方式去实现模块化的代码组织。这些方式在不同的开发者去实施的时候会产生一些细微的差别,为了统一不同的开发者和不同项目之间的差异,就需要一个标准去规范模块化的实现方式。另外在模块化当中针对模块加载的问题,在前面四个阶段都是通过 script 标签手动的去引入每个用到的模块,这也就意味着模块加载并不受代码的控制一旦时间久了过后维护起来就非常的麻烦。试想一下如果某个页面中依赖了一个模块,而在 HTML中却忘记引用这个模块的话就会出现问题。又或者是在页面中移除了某个模块的引用,却忘记了在 HTML 当中去删除这个模块的引用这些都会产生很大的问题。所以说我们需要一些基础的公共代码去实现自动通过代码去帮我们加载模块,也就是说现在需要的是一个模块话的标准和一个自动加载模块的基础库(模块加载器)。

CommonJS 规范

提到了模块化规范,我个人首先想到的就是 CommonJS 规范,它是 node 中所提出的一套标准在 node 中所有的模块代码必须要遵循 CommonJS 规范。这个规范约定了每一个文件就是一个模块、而且每一个模块都有一个单独的作用域、每个模块需要通过 module.exports 导出成员、通过 require 函数载入模块。如果想在浏览器端也使用这个规范的话就会出现一些问题,因为 node 的模块加载机制是同步的方式。 node 的执行机制是在启动时去加载模块执行过程当中是不需要去加载的,它之后去使用到模块所以说这种方式在 node 当中不会有问题。但是如果换到浏览器端,在浏览器中使用 CommonJS 规范的话,必然导致效率低下因为每一次页面加载都会导致大量的同步模式请求出现。

AMD 规范

所以说在早期的前端模块化当中并没有选择 CommonJS 规范,而是专门为浏览器端结合浏览器的特点重新设计了一个规范 AMD(Asynchronous Module Definition) 异步模块定义规范,而且同期还推出了一个 AMD 规范的库 require.js 。它实现了 AMD 这个规范,另外它本身又是一个非常强大的模块加载器。在 AMD 这个规范当中约定每个模块都必须要通过 define 函数去定义,这个函数默认可以接收两个参数也可以传递三个参数。如果传递三个参数的话,第一个参数就是模块的名字在后期加载这个模块的时候使用、第二个参数是一个数组用来声明这个模块的一些依赖项、第三个参数是一个函数并且这个函数的参数与前面的依赖项一一对应每一项为模块导出的成员。define 函数的作用可以理解为当前模块提供一个私有空间,如果说想要向外部导出一些成员的话可以通过 return 的方式实现。

// 定义一个模块
define('module1', ['jquery', './module2'], function($, module2){
	return {
    	start: function() {
        	$('body').animate({margin: '200px'})
        }
    }
})

除此之外 requireJS 当中还提供了一个 require 函数用来帮助开发者自动加载模块,它的用法和 define 函数类似。区别是在于 require 函数只是用来去加载模块,而 define 函数是用来定义模块。

// 载入一个模块
require(['./module1'], function(module1) {
	module1.start()
})

一旦当 requireJS 需要加载一个模块的话,它内部会自动创建一个 script 标签去发送对应的脚本文件的请求并且执行相应的模块代码。 目前绝大多数的第三方库都支持 AMD 规范,但是 AMD 使用起来相对比较复杂,因为开发者在编写代码的时候不光要编写业务代表,并且还要使用 require、define 等这些操作模块的代码手动去编写模块文件。这些会导致我们的代码复杂程度有一个提高,另外如果模块当中如果模块划分的过于细致的话就会造成在同一个页面当中对 JS 文件请求次数就会特别多,从而导致页面效率就会比较低下。所以 AMD 是前端模块化演进中的一步,它是一种妥协的实现方式并不能算是最终的解决方案,只不过在当时的这个背景下面它还是非常有意义的。

CMD 规范

AMD 首次在浏览器端为前端模块化提供了一个标准,同期还出现了一个由淘宝所提出的 sea.js 库所实现的另一个标准 CMD(Common Module Defination)通用模块定义规范。这个模块标准有点类似于 CommonJS,在使用上和 requireJS 也基本上差不多可以说是一个重复的轮子。它当时的想法就是希望让 CMD 写出的代码尽可能的跟 CommonJS 类似从而减轻开发者的学习成本,但是这种方式在后来被 requireJS 也兼容了。

// CMD 规范
define(function(require, exports, module) {
	// 通过 require 引入依赖
    var $ = require('jquery')
    // 通过 exports 或者 module.exports 对外暴露成员
    module.exports = function () {
    	$('body').append('<p>module2</p>')
    }
})