小记JS模块化

有很长一段时间,我对JS模块化的概念就是处于模糊的状态。感觉自己的智商真的有点低。后来,突然发现模块化其实就是C++中的include功能,说白了有点像类的封装。茅塞顿开!!!

本文就稍微整理下JS的模块化。

模块化的背景

当项目变得复杂,可能会出现的问题:

  • 命名冲突
  • 按需加载
  • 依赖管理问题
    于是就有了模块的必要性。除了解决以上两个问题之外,模块化还有的好处是:

  • 可维护性

  • 代码复用

我们来看看目前有哪些解决方案。总的来说,出现了命名空间,模块模式,沙盒,依赖注入,CommonJS,AMD,CMD,UMD,ES6等解决方案。

命名空间(2002)

用对象的方式来模拟命名空间:

netease.add = function(){};
siji.add = function(){};

此方法比较简陋,多人需要约定协调。也仅仅在一定程度上解决了变量冲突的问题。

依赖注入(2004)

写过Angular的人对这个词一定不陌生,自从我有了模块化的概念之后,我对依赖注入的理解就是模块化的一种方式。但确切来说,是引入一个实例化对象。

模块模式(?)

用立即执行函数的方式:

(function($){
   var a = 1; // 私有变量
   function b(){ //私有方法
       // ... 
   }
    return {  //暴露给外部的接口
        b: b
    }
})(jQuery)

但仍没有解决依赖关系,仍然要非常注意脚本的加载顺序。比如上述例子,jQuery必须先加载,不然就会出错。

沙盒(2009)

YUI3的沙盒机制:

YUI().use('node', function(Y){
    // Your code goes here. 
})

仍然没有解决依赖管理的问题。

CommonJS(2009)

用于服务端的模块处理库,每个文件都是一个模块,Node遵循的就是CommonJS。导入模块的方式:

//a.js
function add(x){
    return x+1;
}

module.exports = add;

//b.js
var a = require(a.j); 
return a.add(4);

加载文件是同步加载的,在服务器环境是可行的。但不能应用于浏览器。所以就出来了AMD,CMD的解决方案。

AMD(2009)

AMD是Asynchromous Module Definition的缩写,异步模块定义。RequireJS为代表。推崇依赖前置。

//c.js 
define(['./a', './b'], function(a, b){
    //a, b依赖在一开始就加载好
    a.test();

    b.test();
})


define(id?, deps?, factory) 

define(function(require, exports, module){
    //Your code write here
})

require(['./c'], function(c) {
    //执行回调函数
});

异步加载,只有当依赖模块加载完毕时,才会执行回调函数。

CMD(2011)

CMD是Common Mudule Definition的缩写。SeaJS为代表。推崇依赖就近。

define(function(require, exports, module){
    //需要时候才加载
    var a = require('./a');
    a.test();

    var b = require('./b');
    b.test();
})

seajs.use(['./a','./b'],function(a,b){
     //执行回调函数
});

UMD(2011)

UMD是Universal Module Definition的缩写,通用模块定义。它是AMD和CommonJS的杂糅,希望能同时处理前后端。主要逻辑为:先判断是否支持Node.js的模块是否存在,存在则使用CommonJS;再判断是否支持AMD(define是否存在),存在则使用AMD加载模块。

(function(root, name, factory){
    if (typeof exports === 'object') {
        module.exports = factory(); 
    } else if (typeof define === 'function' && define.amd) {
        define(factory); 
    } else {
        root[name] = factory();
    }    
}(this, 'test', function(){
    // Your code goes here
})

ES6(2015)

ES6终于引入模块化概念,用简单的import,export来处理模块化。不过,ES6尚未被所有浏览器支持,只能通过Babel之类的转译工具来转译。

// a.js
function add(x){
    return x+1;
}

export.add = add;

// b.js
import {add} from './a'
export.increase = function(x){
    return add(x);
}

小结

既然有了JavaScript正室module,我想以后应该就是module的天下了。JavaScript模块化的问题就得到了完美的解决。当然,目前的方案还是比较简单,以后应该会更加完善。

介绍了模块化的基本概况之后,下一篇我会啃一啃模块化的原理和实现。希望我的智商还够用~~~

参考资料: