requireJS(版本是2.1.15)学习教程(一)

 

一:为何要使用requireJS?html

      好久以前,咱们全部的JS文件写到一个js文件里面去进行加载,可是当业务愈来愈复杂的时候,须要分红多个JS文件进行加载,好比在页面中head内分别引入a.js,b.js,c.js等,以下所示:   node

<script src="js/app/a.js"></script>

<script src="js/app/b.js"></script>

 <script src="js/app/c.js"></script>

  咱们如今先在浏览器下看看这些请求,以下所示:jquery

  

这样的写法有以下缺点:数组

       1. 页面在加载的时候,是从页面自上往下加载及渲染的,当页面上有多个分散的js文件时候,页面会先加载及解析头部的JS文件(同步加载),页面被堵塞了,其次分散的js请求数多了,网页失去响应的时间就会变长。浏览器

      2. 因为JS文件存在依赖关系,好比上面的b.js要依赖于a.js,因此务必保证a.js优先引入到页面上来且先加载,要严格保证加载顺序,依赖性最大的文件必定要放到最后加载。可是当依赖关系很复杂的时候,代码的编写和维护就会变得困难了。app

          固然上面引入JS时候,对于第1点:首先:咱们能够放在底部去加载,把全部JS放在</body>以前去,这样就会解决了游览器堵塞的问题,其次咱们能够把全部的JS文件打包成一个JS文件,可是依赖性(也就是顺序)咱们仍是没有办法解决掉,因此咱们引入了requireJS。jquery插件

二:使用requireJS的优势有哪些?异步

       1.  实现JS文件的异步加载,避免网页被堵塞。async

       2.  管理模块之间的依赖性,便于代码的编写和维护。函数

requireJS基本语法及使用.

      1.  首先咱们须要到官网下载最新版本的requireJS源码包。下载地址:

           在页面头部head标签内引入requireJS,以下:<script src="js/require.js"></script>,可是加载这个文件也会形成网页失去响应,咱们能够加上 defer 和 async这个属性。以下:

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

          Async属性代表文件须要异步加载,IE不支持这个属性,只支持defer,因此上面把这2个属性都加上。接下来,看看requireJS启动加载脚本的初始化方式,requireJS支持属性 data-main 这个属性来加载初始化的JS文件,以下:

          <script src="js/require.js" defer async="true" data-main="js/app.js"></script>

            上面的意思是:先异步加载requireJS文件,完成后继续异步加载app.js文件,假如app.js内容为空的话,咱们能够看看加载顺序以下:

       

   上面的app.js后的.js能够去掉,由于requireJS源码已经默认都是之后缀JS文件结尾的。

   2.  如何定义模块文件?

        RequireJS编写模块不一样于其余脚本文件,它良好的使用define来定义一个做用域避免全局空间污染,它能够显示出其依赖关系,并以函数(定义此模块的那个函数)参数的形式将这些依赖进行注入。

下面咱们来看下demo,以下新建一个项目文件:

咱们先在app/b.js下添加基本的requireJS代码以下:

  // b.js

 define(function(){

     var add = function(x,y) {

             return x + y;

    };

    return {

             add : add

    }

 });

使用define来定义模块,下面咱们须要在app.js里面来加载b.js模块,以下在app.js里面来调用了。

require(['app/b'], function (b){

   console.log(b.add(1,1));

});

   咱们接着看看文件加载的状况以下:

在head标签内动态生成文件,以下:

能够看到加载顺序 requirejs --> app.js --> b.js。

    上面的是函数式的定义如上面方式编写代码(使用define定义一个函数),咱们还能够编写简单的键值对,直接返回一个对象(能够解决全局变量的理念),咱们如今在a.js里面返回这么一个对象,以下:

 // a.js

define(function () {

    return {

        color: "black",

        size: "unisize"

    }

});

在app.js初始化代码以下:

require(['app/a'],function(a){

         console.log(a);

});

咱们在控制台上能够看到以下:

  直接返回一个对象,经过使用上面的方法咱们能够想到能够解决全局变量概念,好比全局变量所有使用define函数包围,何时须要全局变量的话,直接require([‘XX’],function(XX){})这样调用下,同时全部的JS都是异步的,并不会堵塞加载。

 3. AMD模块规范

       第一种写法:

       define(function() { 

            return { 

                 mix: function(source, target) { 

              } 

          }; 

     });

    第二种写法 有依赖项 以下:

    define(['data', 'ui'], function(data, ui) { 

       // init here 

     });

   第三种写法 直接一个对象

   define({ 

     data: [], 

     ui: [] 

   });

  第四种写法 具名模块 以下:

  define('index', ['data','base'], function(data, base) { 

      // todo 

  });

  第五种写法 包装模块 以下:

  define(function(require, exports, module) { 

      var base = require('base'); 

      exports.show = function() { 

        // todo with module base 

      }  

  });

     书写格式和nodeJS比较像,可使用require获取模块,使用exports或者module.exports导出API。

     固然requireJS是遵循AMD的规范的,因此通常状况下也具备上面的书写代码方式。

     对于第四种写法 具名模块写法咱们并不推荐的,由于不书写模块名咱们同样能够调用,且在合并代码的时候,咱们也能够根据代码自动生成模块名,若是咱们如今写死了模块名,当某个时候,b.js我要移动到其余目录时候,JS也要跟着改,因此代码维护方面很差,因此不建议书写模块名。对于第五种写法,requireJS中也是支持的,经过内部调用require来处理依赖模块,咱们也能够试着作demo看看就知道了,仍是app.js,我想初始化a.js代码,我改为这样的方式,以下代码:

  define(function(require, exports, module) { 

      var a = require('app/a');

           console.log(a);

      exports.show = function() { 

          // todo with module base 

      }  

  });

经过控制台也能够看到已经打印出 a 出来。

注意:一、 书写requireJS遵循一个文件一个模块。

         二、 不要手动写模块名标示。

4. requireJS配置项以下:

       1.baseUrl: 指定本地模块的基准目录,即本地模块的路径是相对于那个目录的,该属性一般有requireJS加载时的data-main属性指定。好比以下代码:

   项目目录结构仍是上面的。

      

在页面顶部<head>中引入 <script src="js/require.js" defer async="true" data-main="js/app"></script>

   在app.js以下代码:

        requirejs.config({

            baseUrl: 'js/app'

        });

      requirejs(['a','b','c'],function(a,b,c){

      });

在浏览器页面游览能够看到以下请求:

      如上能够看到,index.html和js是同一个目录下的,都是放在requireJS文件夹里面的,因此定义baseUrl:’js/app’ 会自动解析成 requireJS/js/app/ 因此requirejs([‘a’,’b’,’c’])的话,会自动到requireJS/js/app/目录下去查找a.js,b.js,c.js.找到了就能够加载出来。

     若是未显示设置baseUrl,则默认值是加载require.js的html所处的位置,若是使用了data-main属性的话,则该路径变成了baseUrl.以下代码:

    Index.html代码以下:

   <script src="js/require.js" defer async="true" data-main="js/app"></script>

   App.js代码以下:

   requirejs(['a','b','c'],function(a,b,c){

   });

那么在浏览器下会被解析成以下:

    如上显示:默认状况下是从data-main文件入口去加载js/app.js代码的,可是如今app.js中并无设置config配置项,因此使用requirejs([‘a’,’b’,’c’],function(a,b,c))的时候会继续加载js下面的a.js,b.js,c.js,若是找到就加载,没有找到就显示404 not found,如上所示。

    2.paths:  paths是映射那些不直接放在baseUrl指定的目录下的文件,设置paths的起始位置是相对于baseUrl的,除非该path设置是以”/”开头或含有URL协议(http://或者https://).

   以下在app.js代码:

    requirejs.config({

           baseUrl: 'js/lib',

           paths: {

               app: '../app'

           }

    });

   requirejs(['app/a'],function(a){

   });

   在页面上加载显示以下:

   

     能够看到paths是相对于baseUrl配置项生成的,baseUrl:’js/lib’下的全部js文件,可是paths下的 app:’../app’是相对于js/lib下设置的,’..’的解析到js目录下,而后就解析成js/app下,再require([‘app/a’]),就解析到js/app/a.js了。

     若是app.js代码注释掉baseUrl时,变成以下代码:

      requirejs.config({

            //baseUrl: 'js/lib',

           paths: {

               app: '../app'

           }

      });

     requirejs(['app/a'],function(a){  });

     那么就被加载成这个样子了,以下所示:

   

直接把app/a.js放在项目文件requirejs下了。

  3. shim参数 解决了使用非AMD方式定义的模块(如jquery插件)及其载入顺序,为那些没有使用define()来声明依赖关系,设置模块的”浏览器全局变量注入”型脚本作依赖和导出配置。

  在js/app目录下新建文件 depBase.js 代码以下:

  define(function(){

         return {

                  "a":11

         }

   })

  接着在app.js文件里面把代码改为以下:

  require.config({

       baseUrl: 'js/lib',

       shim: {

           'app/depBase': ['jquery']

       },

       paths: {

          app: '../app'

      }

  });

require(['app/depBase'],function(base){

  console.log(base);

});

  而后在浏览器查看请求以下:

    由上面能够看到,我require(['app/depBase'],function(base){console.log(base);});这个,它先加载baseUrl中的配置 js/lib下的jquery文件,而后再加载js/app/depBase.js文件。也就是说shim这个参数能够解决没有使用define(function(){})这样的文件包围的代码或者一些全局变量注入,能够确保此文件先加载,而后再加载其余文件。

    可是若是我不使用shim这个参数的话,在最新版的requirejs2.1.15中(之前的版本我不太清楚),也能够经过require([‘XX’])来解决,以下演示:

     好比我在js/app文件下新建global.js文件,如今的目录以下:

其中global.js代码以下:

  names = 1111;

 创造一个全局变量names,其中js/app/depBase.js代码变成以下:

  define(function(){

       return {

                'name':names

       }

  })

也就是说我在app.js代码以下初始化以下:

require.config({

       baseUrl: 'js/app'

});

require(['global','depBase'],function(global,base){

       console.log(base);

});

我先global初始化引入全局变量names,接着打印出depBase的返回值,截图以下:

也能够看到,能够引入到全局变量names的值。

4.Map参数:Map参数是用来解决同一个模块不一样版本的问题,好比在项目开发中,开发初期使用了jquery1.7版本,可是因为业务的需求须要引入jquery1.9以上的版本时候,可是又担忧有些是依赖于jquery1.7的代码升级到1.9以上的时候会有问题,所以可让一部分代码仍是依赖于jquery1.7,薪增的代码依赖于jquery1.9.

下面咱们来看看咱们目录结构以下所示:

 

我在lib文件下新增jquery1.7.js和 jquery1.9.1.js,如今我在入口文件app.js添加以下代码:

requirejs.config({

    map: {

        'app/a': {

            'jquery': 'js/lib/jquery1.7.js'

        },

        'app/b': {

            'jquery': 'js/lib/jquery1.9.1.js'

        }

    }

});

require(['app/a'],function(jq){   

});

require(['app/b'],function(jq){  

});

而后在app/a.js添加以下代码:

// a.js

define(function (require, exports, module) {

    var a = require(['jquery']);

});

在app/b.js添加以下代码:

// b.js

define(function (require, exports, module) {

    var b = require(['jquery']);

});

在app.js中

require(['app/a'],function(jq){  

});时候,在加载app/a.js的时候会加载jquery1.7.js文件,在加载app/b.js的时候会加载jquery1.9.1.js.以下截图所示:

若是在app.js中把下面这行b.js代码初始化注释掉

require(['app/b'],function(jq){   

});

那么就只会加载app/a.js及对应的jquery1.7.js,截图以下:

  相应的 若是把app/a.js初始化代码注释掉,把app/b.js代码初始化打开,那么只会加载jquery1.9.1,能够看到若是我想app/b.js中使用jquery1.9的话,那么能够这样使用了。

   5.config参数。 config是指须要将配置信息传给一个模块,这些配置每每是application级别的信息,须要一个手段将他们向下传递给模块。在requireJS中,基于requirejs.config()的config配置项来实现。要获取这些信息的模块能够加载特殊的依赖 ”moudle” ,并调用module.config().

首先咱们能够仍是试着作demo来理解下上面话的意思吧,我如今在项目requirejs下js/app文件下新建一个d.js. 而后在app.js初始化文件加入以下代码:

requirejs.config({

    config: {

        'app/c': {

            size: 'large'

        },

        'app/d': {

            color: 'blue'

        }

    }

});

require(['app/c'],function(c){

         console.log(c);

});

require(['app/d'],function(dss){

         console.log(d);

});

在c.js里面这样写代码:

define(function (require, exports, module) {

    //其值是'large'

    var size = module.config().size;

         return size;

});

在控制台下运行能够看到能打印出 large值出来,这说明咱们能够经过config配置项来给app/c.js传递一个模块信息,好比如上面的一个对象{size:large},而在c.js里面直接能够经过module.config()方法来获取size的值。

下面咱们可使用一个依赖数组来作一样的事情,以下d.js代码:

define(['module'], function (module) {

    //Will be the value 'blue'

    var color = module.config().color;

         return color;

});

在控制台看 也同样能够打印出color值出来。

   6. 内部机制:

       RequireJS加载的每一个模块做为script Tag,使用head.appendChild()方法。

       在模块的定义时,requireJS等到全部的依赖都加载完毕,会为函数的调用计算出正确的顺序,而后在函数中经过正确的顺序进行调用。

    7. requireJS函数增长了第三个参数errbacks

    仍是作demo来演示下,咱们仍是在入口文件app.js下增长代码,以下:

 

原本加载b模块是app/b  可是我故意写错成b 因此就不会执行第一个回调函数,转而到第二个回调函数内。以下弹框:

  8.在模块载入失败回调中可使用undef函数移除模块的注册。

   以下代码:

    

代码:

   require(['b'], function ($) {

      //Do something with $ here

    }, function (err) {

         var failedId = err.requireModules && err.requireModules[0];

         if (failedId === 'b') {

              requirejs.undef(failedId);

         }

   });

相关文章
相关标签/搜索