js的模块化探究

作者: physihan | 来源:发表于2016-09-04 20:27 被阅读137次

    这篇文章观点主要来源阮一峰的博客及自己的理解,这里写出来一是为了加强自己的记忆,二是希望和大家一起分享,如果有错误希望大家指出。
    模块通俗来说就是包含有很多方法的集合,感觉有点像别的语言中的库文件,在网页应用中JavaScript的使用越来越广泛,一个网页中所用到的js文件也越来越多,管理起这些文件就显得非常混乱,首先看看js的模块化写法,

    js的模块化

    如果将所有方法都直接写在全局域里面,那么就会污染全局变量,甚至占用很多有用的名字,取名也会费一番脑经,这种方式很不适合大型开发

    function f1(){
    //...
    }
    function f2(){
    //...
    }
    //...
    

    改进的写法就是将这些方法写入一个对象中,但是如果模块里面有一些不希望暴露给使用者的属性或方法,就没有办法了

    var module=new Obeject(
    f:1,//不希望给外面调用
    f1:function(){
    //...
    },
    f2:function(){
    //...
    },
    //...
    );
    

    这就出现了立即执行函数这种写法,这个函数返回一个希望暴露给外部的一系列方法或属性的对象集合(甚至还可以用新的名字去替换原先模块里的名字),并直接定义一个变量去引用这个对象

    var module1=(function(){
    f:1,//不希望给外面调用
    var f1=function(){
    //...
    }
    var f1=function(){
    //...
    },
    //...
    return {
    out_f1:f1,
    out_f2:f2
    };
    })();
    

    这里将f这个属性向外界隐藏了,这种方式还可以对function传入其他全局变量甚至传入它自己,用以分次添加模块的方法,可以很方便的临时添加功能

    //传入其他全局变量,比如类库
    var module2=(fuction($,mod,mod1){
    //使用jquery操作等
    mod.f3=mod1.dosomething;//将别的模块的方法添加到这里来
    return mod;//返回修改后的module2
    })(Jquery,module2,module3)
    
    

    现行的模块规范有两种,一种是CommonJs,另一个是AMD。
    由于nodejs的出现,让JavaScript不再局限于网页端编程,进入了服务器的领域,对于复杂的后端逻辑避免不了大量的模块引用,在nodejs中,模块是采用CommonJs的规范写的,使用下面的方式去加载和使用一个写好的math模块

    var math = require('math');
    math.add(2,3); // 5
    

    这种方式在浏览器环境中是不适合的,要使用这个模块的方法必须等这个加载过程结束才可以,模块存储在服务器端,从浏览器端加载模块会因为各种各样的原因导致加载缓慢,这样页面就会停滞,这会大大影响用户的体验。
    因此在浏览器端不适合同步加载,必须使用异步加载,于是AMD规范出现了,AMD是"Asynchronous Module Definition"的缩写,意思就是“异步模块定义”。 它采用的也是require方法去加载模块,但是它的参数带有一个回调函数,也就是加载成功就会执行那个函数

    require([module], callback);//两个参数,一个模块名,一个回调函数
    
    require(['math'], function (math) {
    math.add(2, 3);
    });
    

    现在主要有两个Javascript库实现了AMD规范:require.jscurl.js

    require.js的使用

    最早的时候,js文件只需要几个就够了,js文件之间的依赖关系也没有那么密切,然而现在的情况不同了,经常会见到下面的代码

    <script src="1.js"></script> 
    <script src="2.js"></script>
    <script src="3.js"></script>
    <script src="4.js"></script>
    <script src="5.js"></script>
    <script src="6.js"></script>
    

    相互之间还会有依赖关系,比如很多js文件都很依赖jquery这个库,所以如果加载顺序不同,会很麻烦,因此要严格遵照依赖性顺序,依赖性最大的放到最后,require.js的出现主要是为了两个问题,一是实现异步加载js文件,防止网页失去响应,二是为了管理模块之间的依赖关系,方便编写和维护。
    和别的js模块一样,要使用它首先要去官网下载一个最新的版本,加载这个文件时可能也会让网页失去反应,可以在网页的最后面去加载,也可以使用下面的方式加载

    <script src="js/require.js" defer async="true" ></script>   
    

    其中defer是为了支持IE,它不支持async属性,这样就可以异步加载这个js文件了,这样只是加载了require.js,还要给data-main属性写一个入口文件,制定网页程序的主模块

    <script src="js/require.js" data-main="js/main"></script>
    

    其中main.js就是我们的主模块,假设也是在js文件下,

    主模块的写法

    如果不依赖任何模块,就可以直接写JavaScript代码,但是这就没必要用require.js了,
    要加载模块,就要使用require函数

    // main.js
    require(['moduleA', 'moduleB', 'moduleC'], function (moduleA, moduleB, moduleC){
    // some code here
    });
    

    一般的加载方式就是这样,首先接受一个模块数组参数,然后有一个回调函数,数组作为函数的参数传入,模块数组中的模块都是异步加载的

    模块的加载

    在下面的例子中

    require(['jquery', 'underscore', 'backbone'], function ($, _, Backbone){
    // some code here
    });
    

    加载了三个模块,但是并没有提这些模块的具体地址、也没有对回调函数的传参使用别名,这里还需要在require.config方法中进行设置

    require.config({
    baseUrl:"js/lib",//相对main.js的目录位置
    path:{
    "jquery": "jquery.min",//在这个目录下的文件名,可忽略js后缀
    "underscore": "underscore.min",
    "backbone": "backbone.min"
    }
    });
    

    还可以直接写cdn地址

    require.config({
    paths: {
    "jquery": "https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min"
    }
    });
    

    AMD模块的写法

    require.js要求加载的模块按照AMD的规范去写
    假设有一个math模块,就要这样去写

    \\mah.js
    define( function()
    {
    var add=function(x,y)
    {
    return x+y;
    };
    return{add:add}
    });
    

    加载

    //main.js
    require(["math"],function(math){
    alert(math.add(1,2));
    });
    

    如果这个模块还依赖其他模块,那么define()函数的第一个参数,必须是一个数组,指明该模块的依赖性。

     define(['myLib'], function(myLib){
       function foo(){
        myLib.doSomething();
        }
        return {
          foo : foo
        };
      });
    

    加载非AMD规范模块

    实际上有很多模块并不是按照AMD规范去写的,require.js加载这些模块需要在require.config中的shim属性中添加相应的依赖关系和导出标记

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

    require.js还提供一系列[插件],实现一些特定的功能。
    domready插件,可以让回调函数在页面DOM结构加载完成后再运行。

      require(['domready!'], function (doc){
        // called once the DOM is ready
      });
    

    text和image插件,则是允许require.js加载文本和图片文件。

      define([
        'text!review.txt',
        'image!cat.jpg'
        ],    function(review,cat){
          console.log(review);
          document.body.appendChild(cat);
        }
      );
    

    类似的插件还有json和mdown,用于加载json文件和markdown文件。

    相关文章

      网友评论

        本文标题:js的模块化探究

        本文链接:https://www.haomeiwen.com/subject/reylettx.html