前端的发展日新月异,前端项目的复杂度也不可同日而语。很多时候,复杂的前端项目都是开发人员开发自己的业务核心逻辑,其余的都是调用了很多开源的现成模块来拼装。这样做有不仅效率高,而且质量也有一定的保证。毕竟下载量大的模块是经过了很多人很多项目的验证。就像java很多好用现成的jar包,python也有很多热门好用的工具包一样。

那么很多人去贡献代码模块,千人前面,良莠不齐也在所难免,再加上前端使用的一些语言在服务器端的大力发展,比如nodejs,模块的跨平台也是需要解决的问题。规范化模块是前端模块化必须面对的大问题。现在前端有npm和yarn等包管理工具来管理和加载很多开源的模块。同时也出来很多模块化的规范。
本文主要介绍4中规范,CommonJS,AMD,CMD,UMD。

一:CommonJS
CommonJS API定义很多普通应用程序(主要指非浏览器的应用)使用的API,从而填补了这个空白。它的终极目标是提供一个类似Python,Ruby和Java标准库。 – 百度百科
如果你写过nodejs程序,你应该对这个不陌生。
语法:

  1. 定义一个模块
    在nodejs里,一个模块就是一个文件
    例如 下面 util.js文件代码
var util = {
    name:'helloModule'
    sayHello:function () {
        return 'Hello commonjs';
    }
}
// exports 是指向module.exports的一个快捷方式
module.exports = util
// 或者
exports.name = util.name;
exports.sayHello = util.sayHello;
  1. 调用一个模块
    通过内置的require方法来引用之前定义好的模块
var selfUtil = require('./util');
selfUtil.name;            
selfUtil.sayHello(); 

这里的模块寻找,缓存等很多操作,都是node帮我们实现好了。本文不做展开探讨
可以看到在commonJS里,定义模块,对外暴露方法或者属性,引用模块还是特别简洁的。
需要注意的是,在nodejs里,引入模块是同步的过程。因为模块文件和执行的程序在一个服务器里,没有网速的限制,IO消耗也几乎忽略不计,所以同步加载一般不会堵塞下面的程序运行

那么问题来了,如果是在浏览器端咋办。我们加载一个模块就等于是加载一个js,需要加载js文件,从浏览器请求到服务端返回,再执行,这里面的延迟往往会是秒级别的,这会导致页面的卡顿和白屏等让人感觉很不流畅的效果。所以在浏览器端,目前是采用了异步加载模块。

二:AMD规范
全称是Asynchronous Module Definition,即异步模块加载机制,AMD 主要就是用在浏览器端的一种模块化规范。
使用AMD规范比较有代表的就是require.js
1.如何定义一个模块

// id 是模块标识,不是必须的
// dependencies 是该模块依赖的其他模块,不是必须的
// factory是依赖模块加载完的回调函数
define(?id, dependencies?, factory );

// 定义了一个叫util的模块
// 依赖jquery模块
// 回调函数是第一个参数对应的是jquery模块暴露的内容
// 通过回调函数return 来暴露对外内容
define(‘util’, ['jquery'], function($){
	return  {
		name: 'amd',
		sayHello: function(){
	      console.log('hello amd')
        }
    }
 } );
  1. 如何引用模块
require(['util'],function(util){
	util.sayHello()
})

requirejs是依赖前置,也就是会提前将所有的依赖都加载完,然后在回掉函数里直接就可以使用到,还有更多详细的用法本文不做展开。

三:CMD
CMD 是seajs推广的时候提出来的一个规范。它的主要特点就是模块只有在用的时候才进行加载,也就是说用的时候才去调用载入模块

  1. 定义模块
    一个文件就是一个模块
    cmd也是通过define来定义模块,回掉函数里默认有三个内置变量,require,exports 和module,这和commonjs保持了一致。
    cmd规范加载模块是分同步和异步,同步是会进行预加载,预加载完后不执行,等到了调用require的地方才会引入执行,异步加载会等到加载完了然后调用回调函数执行。
define(function(require, exports, module) {
  var $ = require('jquery'); // 会提前预先分析预加载,等执行到这句话的时候才执行
  // 异步加载依赖模块,
  require.async('jquery',function(){
		
  })
  // 通过exports 暴露对外接口
  exports.sayHello = function() {
    $('#hello').toggle('slow');
  };
});
  1. 使用模块
seajs.use(['./hello', 'jquery'], function(hello, $) {
});

本来是准备搞规范让天下一统的,这下一下子冒出来三个规范。咋办,如何统一,比如跨平台定义模块和浏览器模块化规范语法差别很大,怎么统一,UMD应运而生。

四:UMD
通用模块定义规范(Universal Module Definition)
UMD主要是为了解决CommonJS和AMD的兼容。
// 下面是UMD的定义模块的方式
以Vue框架的开头为例:

(function (global, factory) {
 //  检测 module 和 exports 判断是否是commonjs规范的场景
  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
  // 检测是否是amd的规范场景
  typeof define === 'function' && define.amd ? define(factory) :
  // 其他场景 比如浏览器window全局定义场景或者其他作用域挂载场景
  (global = global || self, global.Vue = factory());
}(this, function () { 
  // 定义代码
  // 返回暴露的代码
  return Vue
})

除了上面这些之外,ES6 也给我们提供了一些模块化的写法,因为目前兼容性太差,不做探讨。

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐