zoukankan      html  css  js  c++  java
  • requireJS(版本是2.1.15)学习教程(一)

    一:为什么要使用requireJS?

          很久之前,我们所有的JS文件写到一个js文件里面去进行加载,但是当业务越来越复杂的时候,需要分成多个JS文件进行加载,比如在页面中head内分别引入a.js,b.js,c.js等,如下所示:   

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

      我们现在先在浏览器下看看这些请求,如下所示:

      

    这样的写法有如下缺点:

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

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

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

    二:使用requireJS的优点有哪些?

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

           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 出来。

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

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

    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);

             }

       });

  • 相关阅读:
    ffmpeg rtmp推流 视频转码
    java日志发展史 log4j slf4j log4j2 jul jcl 日志和各种桥接包的关系
    nginx stream 流转发,可以转发rtmp、mysql访问流,转发rtmp、jdbc请求
    java web http 转https 通过nginx代理访问
    linux 服务器磁盘挂载
    novnc 通过websockify代理 配置多点访问
    linux 文件服务 minio 安装部署配置
    AOP实现原理,手写aop
    java 泛型
    JAVA反射getGenericSuperclass()用法
  • 原文地址:https://www.cnblogs.com/tugenhua0707/p/4067220.html
Copyright © 2011-2022 走看看