个人对模块化理解

195 阅读5分钟

最近精神状态不是太好,晚上总也睡不好,但是学习还是不能落下滴!毕竟 逆水行舟,不进则退。没毛病!安排!!!

1. 模块的写法发展

  • 最开始的写法
function m1() {..........}

function m2() {..........}

这两个函数组成一个模块,使用时直接调用,但是这样明显污染全局环境,无法保证不与其他的模块发生变量名冲突,并且这样也看不出来各个模块之间的依赖关系。

  • 写成对象的形式
var module = new Object({
    _count: 0,
    m1: function() {
        //...
    },
    m2: function() {
        //...
    }
});

这样就写module.m1(); 但是这样暴露了所有模块成员,内部代码可以被外部改写。比如外部代码可以直接改变内部计数的值 :module1._count =5;

  • 使用立即执行函数可以达到不暴露私有成员变量的目的
var module1 = (function() {    
    var _count = 0;    
    var m1 = function() {       //...
            };    
    var m2 = function() {       //...
            };    
    return {      
        m1: m1,
       m2: m2    
    };
})();
  • 后来又有了宽放大的模式
var module = (function(mod) {     
    //...        
    return mod;
})(window.module || {});

2. 出现模块化规范,目前有 :CommonJS模块、AMD模块还有CMD模块 几种规范。

  • CommonJs缺点及AMD出现背景

CommonJS规范同步加载模块,这对服务器端不是一个问题,因为所有的模块都存放在本地硬盘,可以同步加载完成,等待时间就是硬盘的读取时间。但是,对于浏览器,这却是一个大问题,因为模块都放在服务器端,等待时间取决于网速的快慢,可能要等很长时间,浏览器处于"假死"状态。因此,浏览器端的模块,不能采用"同步加载"(synchronous),只能采用"异步加载"(asynchronous)。这就是AMD规范诞生的背景

var math = require('math');
math.add(2, 3)
  • AMD写法(AMD推崇依赖前置,在定义模块的时候就要声明其依赖的模块)

AMD异步加载模块加载采用异步方式加载模块,模块的加载不影响它后面语句的运行。所有依赖这个模块的语句,都定义在一个回调函数中,等到加载完成之后,这个回调函数才会运行。 AMD也采用require加载模块 require([module], callback);第一个参数[module],是一个数组,里面的成员就是要加载的模块;第二个参数callback,则是加载成功之后的回调函数

require(['math'], function(math) {   
    math.add(2, 3);
});

math.add()与math模块加载不是同步的,浏览器不会发生假死。所以很显然,AMD比较适合浏览器环境。==requireJS这个JavaScript的库实现了AMD规范==

  • CMD(CMD推崇就近依赖,只有在用到某个模块的时候再去require)

CMD有个浏览器的实现SeaJSSeaJS要解决的问题和requireJS一样,只不过在模块定义方式和模块加载(可以说运行、解析)时机上有所不同。在CMD中一个模块就是一个文件

define(function(require, exports, module) {

    // 模块代码

});

require是可以把其他模块导入进来的一个参数;而exports是可以把模块内的一些属性和方法导出的;module 是一个对象,上面存储了与当前模块相关联的一些属性和方法。

// CMD定义模块
define(function(require, exports, module) {
    var a = require('./a')
    a.doSomething()
    // 此处略去 100 行
    var b = require('./b') // 依赖可以就近书写
    b.doSomething()
    // ... 
})

// AMD 定义模块
define(['./a', './b'], function(a, b) { // 依赖必须一开始就写好
    a.doSomething()
    // 此处略去 100 行
    b.doSomething()
        ...
})
//定义模块  myModule.js
define(function(require, exports, module) {
  var $ = require('jquery.js')
  $('div').addClass('active');
  exports.data = 1;
});

// 加载模块
seajs.use(['myModule.js'], function(my){
    var star= my.data;
    console.log(star);  //1
});

3. 对于上边AMD中requireJS用法说明

  • 为什么用requireJS
<script src="1.js"></script>
<script src="2.js"></script>
<script src="3.js"></script>
<script src="4.js"></script>

这样的方式加载有两个缺点:加载的时候,浏览器会停止网页渲染,加载文件越多,网页失去响应的时间就会越长(解决方法在下边);其次,由于js文件之间存在依赖关系,因此必须严格保证加载顺序

  • requireJS解决依赖问题 require.js解决了这个问题 async属性表明这个文件需要异步加载,避免网页失去响应。IE不支持这个属性,只支持defer,所以把defer也写上。加载require.js以后就需要 加载我们自己的代码了,写一个main.js 这就相当于c语言的main()入口;
data-main属性的作用是,指定网页程序的主模块
//main.js 
require(['moduleA', 'moduleB', 'moduleC'], function(moduleA, moduleB, moduleC) {     
    // 此处省略1000行code
});

require()函数接受两个参数。第一个参数是一个数组,表示所依赖的模块,上例就是['moduleA', 'moduleB', 'moduleC'],即主模块依赖这三个模块;第二个参数是一个回调函数,当前面指定的模块都加载成功后,它将被调用。加载的模块会以参数形式传入该函数,从而在回调函数内部就可以使用这些模块。 require()异步加载moduleA,moduleB和moduleC,浏览器不会失去响应;它指定的回调函数,只有前面的模块都加载成功后,才会运行,解决了依赖性的问题。

  • requireJS如何自定义模块的加载行为:使用require.config()方法进行定义;写在main.js的头部。
require.config({    
    paths: {
        // 或者写上baseUrl: "js/lib",下边就不用写lib            
        "jquery": "lib/jquery.min",
        "underscore": "lib/underscore.min",
        "backbone": "lib/backbone.min"    
    }  
});
  • 很多库的模块不符合AMD规范那么怎么进行加载?

那加载他们之前必须要先定义他们的特征,比如加载underscore和backbone。 require.config()接受一个配置对象,这个对象除了有前面说过的paths属性之外,还有一个shim属性,专门用来配置不兼容的模块。具体来说,每个模块要定义(1)exports值(输出的变量名),表明这个模块外部调用时的名称;(2)deps数组,表明该模块的依赖性。

require.config({    
    shim: {      
        'underscore': {        
            exports: '_'      
        },
        'backbone': {        
            deps: ['underscore', 'jquery'],
            exports: 'Backbone'      
        }    
    }
});

4. 上边的加载方式说完,那么再说一下怎么自己写模块 (使用AMD规范),写模块必须采用特定的define()函数来定义

// math.js模块
  
define(function() {    
    var add = function(x, y) {      
        return x + y;    
    };    
    return {      
        add: add    
    };  
});