Heim  >  Artikel  >  Web-Frontend  >  Sekundäre Kapselung der Ajax- und Ajax-Cache-Proxy-Komponente von jquery: AjaxCache detaillierte Erklärung_jquery

Sekundäre Kapselung der Ajax- und Ajax-Cache-Proxy-Komponente von jquery: AjaxCache detaillierte Erklärung_jquery

WBOY
WBOYOriginal
2016-05-16 15:06:001911Durchsuche

Obwohl die neuere API von jquery bereits sehr einfach zu verwenden ist, ist es bei der tatsächlichen Arbeit immer noch notwendig, eine sekundäre Kapselung durchzuführen. Die Vorteile sind: 1. Die API nach der sekundären Kapselung ist prägnanter und entspricht eher den persönlichen Nutzungsgewohnheiten 2. Sie können eine einheitliche Verarbeitung von Ajax-Operationen durchführen, z. B. das Anhängen von Zufallszahlen oder anderen Parametern. Gleichzeitig werden wir bei der Arbeit feststellen, dass es einige von Ajax angeforderte Daten gibt, für die keine hohen Echtzeitanforderungen gelten. Selbst wenn wir die angeforderten Daten zum ersten Mal zwischenspeichern, werden wir die gleiche Anforderung erneut stellen Die zuvor zwischengespeicherten Daten werden direkt abgerufen. Durch diese manuelle Cache-Steuerung werden Ajax-Anfragen reduziert, was uns auch dabei helfen kann, die Leistung der Webseite zu verbessern. Dieser Artikel stellt meinen eigenen Ansatz zu diesen beiden Themen vor. Ich freue mich über Austausch und Korrekturen.

Klicken Sie hier, um den Code herunterzuladen (Hinweis: Da Ajax verwendet wird, kann es nicht unter dem Dateiprotokoll ausgeführt werden und muss unter http ausgeführt werden)

1. Ajax-Kapselungs-JQuery

Tatsächlich wurde der relevante Inhalt dieses Teils in einem früheren Blog vorgestellt, aber nur ohne detaillierte Erklärung zitiert. Darüber hinaus basiert die Implementierung der Ajax-Cache-Proxy-Komponente auch auf der Ajax-Komponente nach dieser sekundären Kapselung , also gibt es Es ist notwendig, es hier ausführlich zu erklären, obwohl die Implementierung nicht kompliziert ist (Einzelheiten finden Sie in den Kommentaren):

define(function (require, exports, module) {

  var $ = require('jquery');

  //根据关键的几个参数统一创建ajax对象
  function create(_url, _method, _data, _async, _dataType) {
    //添加随机数
    if (_url.indexOf('?') > -1) {
      _url = _url + '&rnd=' + Math.random();
    } else {
      _url = _url + '?rnd=' + Math.random();
    }

    //为请求添加ajax标识,方便后台区分ajax和非ajax请求
    _url += '&_ajax=1';

    //返回jquery创建的ajax对象,以便外部拿到这个对象以后可以通过
    //.done .fail .always来添加回调
    //这么做是为了保留jquery ajax中好用的部分
    return $.ajax({
      url: _url,
      dataType: _dataType,
      async: _async,
      method: _method,
      data: _data
    });
  }

  //ajax就是本组件全局唯一的实例,它的实例方法通过后面的循环代码添加
  //methods对象配置ajax各个实例方法的参数:
  //name: 方法名称
  //method: http请求方法,get or post
  //async: 发送请求时是否异步
  //dataType: 返回的数据类型,html or json
  var ajax = {},
    methods = [
      {
        name: 'html',
        method: 'get',
        async: true,
        dataType: 'html'
      },
      {
        name: 'get',
        method: 'get',
        async: true,
        dataType: 'json'
      },
      {
        name: 'post',
        method: 'post',
        async: true,
        dataType: 'json'
      },
      {
        name: 'syncGet',
        method: 'get',
        async: false,
        dataType: 'json'
      },
      {
        name: 'syncPost',
        method: 'post',
        async: false,
        dataType: 'json'
      }
    ];

  //由于二次封装需要对外提供的每个实例方法创建ajax的逻辑是相同的
  //所以通过这种方式统一定义各个实例方法
  //关键代码为下面代码中的那个立即调用的函数
  //它返回了一个新的闭包函数作为实例方法
  for (var i = 0, l = methods.length; i < l; i++) {
    ajax[methods[i].name] = (function (i) {
      return function () {
        /**
         * 每个实例方法接收三个参数
         * 第一个表示要请求的地址
         * 第二个表示要提交到后台的数据,是一个object对象,如{param1: 'value1'}
         * 第三个表示后台返回的数据类型,最最常用的就是html or json,绝大部分情况下这个参数不用传,会使用methods里面定义的dataType
         */
        var _url = arguments[0],
          _data = arguments[1],
          _dataType = arguments[2] || methods[i].dataType;

        return create(_url, methods[i].method, _data, methods[i].async, _dataType);
      }
    })(i);
  }

  return ajax;
});

Die Hauptfunktionen dieser Ajax-Komponente sind:
1) Geben Sie einheitlich Zufallszahlenparameter und Ajax-Anforderungskennungen an 2) Die JQuery-API ist gepackt und die der Außenwelt bereitgestellten Methoden sind immer klarer.
Verwendung:

define(function (require, exports, module) {

  var Ajax = require('mod/ajax');

  //以GET方式请求html内容
  Ajax.html('html/demo', {
    param1: 'value1',
    param2: 'value2'
  }).done(function(response){
    //请求成功的回调
  }).fail(function(){
    //请求失败的回调
  }).always(function(){
    //请求完成的回调
  });

  //以GET方式请求json数据
  Ajax.get('api/demo', {
    param1: 'value1',
    param2: 'value2'
  }).done(function(response){
    //请求成功的回调
  }).fail(function(){
    //请求失败的回调
  }).always(function(){
    //请求完成的回调
  });

  //以POST方式请求json数据
  Ajax.post('api/demo', {
    param1: 'value1',
    param2: 'value2'
  }).done(function(response){
    //请求成功的回调
  }).fail(function(){
    //请求失败的回调
  }).always(function(){
    //请求完成的回调
  });

  //以GET方式发送同步请求,获取json数据
  Ajax.syncGet('api/demo', {
    param1: 'value1',
    param2: 'value2'
  }).done(function(response){
    //请求成功的回调
  }).fail(function(){
    //请求失败的回调
  }).always(function(){
    //请求完成的回调
  });

  //以POST方式发送同步请求,获取json数据
  Ajax.syncPost('api/demo', {
    param1: 'value1',
    param2: 'value2'
  }).done(function(response){
    //请求成功的回调
  }).fail(function(){
    //请求失败的回调
  }).always(function(){
    //请求完成的回调
  });

});

由于这个组件的每个实例方法返回的对象就是$.ajax创建的对象,所以我们完全可以照常使用.done .fail .always来添加回调,就跟直接用$.ajax没有任何区别。为什么API要设计成html, get, post, syncGet, syncPost这几个方法,而且连dataType基本都是固定的?

那是因为在项目中,我们完全可以约定在异步请求的时候只能用html或json这两种dataType,其它dataType不允许,现在的web项目这两种方式已经完全够用了,至少我没有碰到过非得使用别的dataType不可的情况;而且在实际工作当中html, get, post, syncGet, syncPost这几个方法几乎能够涵盖我们需要的所有异步请求的场景,每当我们要用ajax的时候,无非考虑的就是get还是post,同步还是异步,请求的是html还是json这三个问题,通过它们就能把每个问题都解决了。当然jsonp,rest API这两种情况就另说了,这个组件不是为它们服务的,这也是它的局限性,它还是倾向于在传统的web项目中使用。

2. ajax缓存代理

要实现一个简单的ajax缓存代理组件,首先要清楚这个缓存代理的作用,在本文开篇说到过缓存代理的应用场景:当使用缓存代理第一个发起某个请求时,在请求成功后将数据缓存下来,然后当再次发起相同请求时直接返回之前缓存的数据,缓存代理的作用是控制何时发送请求去后台加载数据,何时不发送请求直接从缓存中读取之前加载的数据。为了实现一个简单的缓存代理,有三个问题要解决:

1)代理对象必须与被代理的对象有相同的API
拿前面的Ajax组件来说,它提供有html, get , post, syncGet, syncPost方法,那么它的代理对象也必须同时具有这些方法,而且调用方式,传入参数都必须完全一致,只有这样,当我们在使用代理对象的时候,就跟在使用原组件对象没有区别。而且在缓存代理内部,在某些条件下是需要调用原组件对象发送ajax请求的,如果接口不同,调用方式不同,参数不同,如何能保证内部能够正确调用原组件对象呢?这个条件还有一个好处,就是当我们下次不想使用代理对象的时候,能够以最小的代价将代理对象替换为原组件对象。
这一点其实是设计模式中代理模式的基本要求。

2)缓存数据存储时的缓存索引问题
也就是说我们以什么样的索引才能保证同一个请求的数据在缓存之后,下次查找时还能根据请求信息查找到呢?ajax缓存有别于其它缓存的地方在于它请求的地址可能包含可变的参数值,同一个地址如果后面的参数不同,那么对应的请求结果也就不一定相同,所以简单起见,可以考虑把请求地址跟请求参数统一作为缓存索引,这样就能对缓存进行简单管理。同时考虑到其它可变性,还应有其它的一些要求,详见后面组件实现中的注释说明。

3)缓存有效时间
虽然要实现的缓存代理很简单,但是这个问题一定是要考虑的,每个缓存代理实例,能够缓存数据的有效时间不一定相同,有的可能只缓存几分钟,有的可能缓存几十分钟,当缓存时间失效时,缓存代理就得删除原来的缓存,然后重新去加载数据才行。

综合这些问题,基于第一部分的Ajax组件,最终实现的缓存代理组件AjaxCache的代码如下(有注释详解):

define(function (require, exports, module) {

  var $ = require('jquery');
  var Ajax = require('mod/ajax');

  //缓存列表
  var cache = {};

  /**
   * 生成缓存索引:
   * 由于索引是根据url和data生成的(data是一个对象,存放Ajax要提交到后台的数据)
   * 所以要想同一个url,同样的data能够有效地使用缓存,
   * 切勿在url和data中包含每次可变的参数值,如随机数等
   * 比如有一个请求:
   * url: aaa/bbb/cccc&#63;r=0.312738
   * data: {name: 'json'}
   * 其中url后面的r是一个随机数,每次外部发起这个请求时,r的值都会变化
   * 由于r每次都不同,最终会导致缓存索引不相同,结果缓存就无法命中
   * 注:随机数可放置在原始的Ajax组件内
   *
   * 还有:如果是同一个接口,最好在同一个页面内,统一url的路径类型,要么都是相对路径,要么都是绝对路径
   * 否则也会导致缓存无法有效管理
   */
  function generateCacheKey(url, data) {
    return url + $.param(data);
  }

  return function (opts) {
    opts = opts || {};

    var cacheInterval = opts.cacheInterval || (1000 * 60 * 60);//缓存有效时间,默认60分钟

    var proxy = {};
    for (var i in Ajax) {
      if (Object.prototype.hasOwnProperty.call(Ajax, i)) {
        //在proxy对象上定义Ajax组件每一个实例方法的代理
        //注意这个立即调用的函数表达式
        //它返回了一个闭包函数就是最终的代理方法
        proxy[i] = (function (i) {
          return function () {
            var _url = arguments[0],
              _data = arguments[1],
              cacheKey = generateCacheKey(_url, _data),
              cacheItem = cache[cacheKey],
              isCacheValid = false;

            if (cacheItem) {
              var curTime = +new Date();
              if (curTime - cacheItem.cacheStartTime <= cacheInterval) {
                //如果请求时间跟缓存开始时间的间隔在缓存有效时间范围内,就表示缓存是有效的
                isCacheValid = true;
              } else {
                //否则就把缓存清掉
                delete cache[cacheKey];
              }
            }

            if (isCacheValid) {
              //模拟一个异步任务来返回已经缓存的数据
              //通过$defer延迟对象,可以保证这个模拟任务返回的对象跟原始Ajax组件调用返回的对象有相同的API
              //这是代理的关键:代理对象与被代理的对象应该具有相同API
              //只有这样当我们取消代理的时候,不会对那些用了代理的组件进行修改
              var $defer = $.Deferred();
              setTimeout(function () {
                $defer.resolve(cacheItem.res);
              }, 10);
              return $.when($defer);
            }

            //缓存失效或者没有缓存的时候调用原始的Ajax组件的同名方法去后台请求数据
            return Ajax[i].apply(Ajax, arguments).done(function (res) {
              //在请求成功之后将结果缓存,并记录当前时间作为缓存的开始时间
              cache[cacheKey] = {
                res: res,
                cacheStartTime: +new Date()
              }
            });
          }
        })(i);
      }
    }
    return proxy;
  };
});

在第一部分和本部分的实现中,最关键的都是那个立即调用的函数表达式,没有它返回的闭包,代码就会有问题,这也是闭包在循环中应用的经典问题。

3. 演示效果

为了说明缓存代理的使用效果,我做了一个演示效果:
Sekundäre Kapselung der Ajax- und Ajax-Cache-Proxy-Komponente von jquery: AjaxCache detaillierte Erklärung_jquery 
其中的ajax.js就是第一部分的实现,ajaxCache.js就是第二部分的实现,演示页面对应代码中的html/demo.html,相关js是js/app/demo.js:

define(function (require, exports, module) {

  var AjaxCache = require('mod/ajaxCache');

  //创建代理对象
  var Ajax = new AjaxCache({
    cacheInterval: 10 * 1000
  });

  var count = 5;

  console.log('时间点:第' + 0 + 's,定时器开始!');
  var t = setInterval(function(){
    if(count == 0) {
      console.log('时间点:第' + (5 - count + 1) * 4 + 's,定时器结束!');
      return clearInterval(t);
    } else{
      console.log('时间点:第' + (5 - count + 1) * 4 + 's:');
    }

    Ajax.get('../api/data.json', {
      name: 'felix'
    }).done(function(res){
      if(res.code == 200) {
        console.log(5 - count + '. data is : ' + JSON.stringify(res.data));
      }
    });

    count --;
  },4000);

});

In diesem Code habe ich ein Proxy-Objekt erstellt, das Ajax-Anfragen 10 Sekunden lang zwischenspeichern kann. Ich habe einen Timer verwendet, um die Get-Methode des Proxy-Objekts fünfmal aufzurufen, um dieselbe Anfrage zu senden.
Sekundäre Kapselung der Ajax- und Ajax-Cache-Proxy-Komponente von jquery: AjaxCache detaillierte Erklärung_jquery
Den Ergebnissen zufolge wurde der gesamte Code 24 Sekunden lang ausgeführt, und die Zeitpunkte, zu denen der Agent die Anfrage sendete, waren jeweils die 4., 8., 12., 16. und 20. Sekunde. Da die Cache-Gültigkeitszeit des Proxys 10 Sekunden beträgt und die 4 Sekunden das erste Mal ist, dass eine Anfrage gesendet wird, wird zu diesem Zeitpunkt definitiv eine echte Ajax-Anfrage gesendet, wenn der Proxy bei den 8 Sekunden und 12 Sekunden dieselbe Anfrage sendet Die Cache-Zeit ist nur 4 Sekunden und 8 Sekunden vergangen, daher ist der Cache zu diesen beiden Zeitpunkten noch gültig, aber als die 16-Sekunden-Anfrage gesendet wurde, waren seit der Cache-Zeit der ersten Anfrage 12 Sekunden vergangen Der Cache war abgelaufen, daher sendete der Proxy eine weitere echte Ajax-Anfrage, und dann wurde der Cache aktualisiert. Die 20-s-Anfrage lag noch innerhalb der letzten Cache-Gültigkeitszeit, sodass keine tatsächliche Ajax-Anfrage gesendet wurde. Schließlich können Sie im Netzwerk sehen, dass der Proxy 5 Anfragen gesendet hat, aber nur 2 Dienste angefordert hat. Wenn die Cache-Gültigkeitszeit verlängert wird, kann die Anzahl der Hintergrundanfragen reduziert werden. Dies ist auch der Schlüssel zur Verbesserung der Front-End-Leistung der Caching-Proxy.

Ich hoffe, dass diese Demonstration Ihnen ein klareres Verständnis der Rolle des Caching-Proxys vermitteln kann.

4. Zusammenfassung dieses Artikels

Die im ersten Teil dieses Artikels zusammengefasste Implementierung wurde in meiner eigenen Arbeit häufig verwendet. Zumindest bin ich auf keine Probleme gestoßen. Es kann jedoch sein, dass ich auf sie nicht gestoßen bin hat immer noch viele Einschränkungen. Ich habe gerade die Implementierung von Teil 2 auf meine Arbeit angewendet. Es gab eine Funktion, die ich als notwendig erachtete, also habe ich eine relativ einfache Implementierung geschrieben. Obwohl sie einfach ist, kann sie tatsächlich mein Problem lösen. So ist Arbeit Im nächsten Blog werde ich die Implementierungsidee einer anderen Komponente mit Cache-Proxy vorstellen, die ähnliche Funktionen wie Provinz- und Kommunalkaskaden hat. Ich möchte jedoch eine vielseitigere Komponente schreiben, die sowohl von der HTML-Struktur als auch von CSS getrennt werden kann Bitte achten Sie weiterhin darauf.

Der obige Artikel kapselt die Ajax- und Ajax-Caching-Proxy-Komponenten von jquery neu: Die detaillierte Erklärung von AjaxCache ist der gesamte vom Herausgeber geteilte Inhalt. Ich hoffe, dass er Ihnen eine Referenz geben kann, und ich hoffe, dass Sie Script Home unterstützen.

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn