ajax 源码解读之如何扩展 ajax 的功能

wangfulin

需求场景:借助 ajaxTransport 来自定义扩展功能。

  1. 项目需求:jQuery 版本: 1.10.2,错误打点,发起 jsonp 请求发生错误的时候前端需要向打点地址发送一个请求。

  2. 简单演示:
    测试代码:

       $.ajax('http://google.com/a.js', {
           type: 'GET',
           dataType: 'jsonp',
           success: function(success, statusText, jqXHR){
               console.log('jsonp request success');
           },
           error: function(jqXHR, statusText, error){
               console.log('jsonp error');
           }
       });
       
       由于 1.10.2 版本的 jquery 并没有对创建的 script 监听错误事件,故无法调用 error 方法,故无法进一步将前端信息错误上报
       

    解决方案:

       $.ajaxTransport('+script', function(s){
           // This transport only deals with cross domain requests
           var script,
                head = document.head || $('head')[0] || document.documentElement;
                
           return {
               send: function(_, callback) {
                   
                   script = document.createElement('script');
                   
                   script.async = true;
                   
                   if(s.scriptCharset){
                       script.charset = s.scriptCharset;
                   }
                   
                   script.src = s.url;
                   
                   // Handle error
                   script.onerror = function(err){
                       // Handle memory lead in IE
                       script.onload = script.onreadystatechange = null;
                       script.onerror = null;
                       
                       // Remove the script
                       if(script.parentNode){
                           script.parentnode.removeChild(script);
                       }
                       
                       // Dereference the script
                       script = null;
                       
                       if(err.type === 'error'){
                           callback(404, err.type);
                       }
                   }
                   
                   // Attach handlers for all browsers
                   script.onload = script.onreadystatechange = function(_, isAbort){
                       
                       if(isAbort || !script.readyState || 
                       /loaded|complete/.test(script.readyState)){
                           // Handle memeory leak in IE
                           script.onload = script.onreadystatechange = null;
                           
                           // Remove the script
                           if(script.parentNode){
                               script.parentNode.removeChild(script);
                           }
                           
                           // Dereference the script
                           script = null;
                           
                           // Callback if not abort
                           if(!isAbort){
                               callback(200, 'success');
                           }
                       }
                   };
                   
                   // Cicumvent IE6 bugs with base elements (#2709 and #4378) by prepending
                   // Use native DOM manipulation to avoid our domManip AJAX trickery
                   head.insertBefore(script, head.firstChild);
               },
               
               abort: function(){
                   if(script){
                       script.onload(undefined, true);
                   }
               }
           };
       });
    

需求场景:借助 ajaxSetup 新增 dataType.

  1. 项目需求:需要从服务器获取 yaml 文件,然后解析该文件

  2. 简单演示:
    解决方案:

       function parseYaml(text){
           console.log('You are parsing yaml file!');
           return 'yaml' + text + 'yaml';
       }
       $.ajaxSetup({
           accepts: {
               yaml: 'application/x-yaml, text/yaml'
           },
           contents: {
               yaml: /yaml/
           },
           converters: {
               'text yaml': function(text){
                   return parseYaml(text);
               }
           }
       });

    测试代码:

       // 发送 dataType 为 yaml 的请求
       $.ajax({
           url: 'http://google.com/helloworld.yaml',
           dataType: 'yaml',
           success: function(data){
               console.log(data);
           }
       });
    

需求场景:借助 ajaxPrefilter 来自定义扩展功能。

  1. 项目需求:防止 ajax 请求的重复提交

  2. 简单演示:
    解决方案:
    var pendingRequests = {};

    function storePendingRequest(key, jqXHR){

       pendingRequests[key] = true;
       jqXHR.pendingRequestKey = key;

    }

    function generatePendingRequestKey(options){

       return (options.type + options.url + options.dataType).toLowerCase().replace(/[^a-z0-9]/g, '');

    }
    $.ajaxPrefilter(function( options, originalOptions, jqXHR ) {

       
       // 不重复发送相同请求
       var key = generatePendingRequestKey(options);
       if (!pendingRequests[key]) {
           storePendingRequest(key, jqXHR);
       } else {
           // or do other
           jqXHR.abort();
       }
       
       var complete = options.complete;
       options.complete = function(jqXHR, textStatus) {
           // clear from pending requests
           pendingRequests[jqXHR.pendingRequestKey] = null;
           
           if ($.isFunction(complete)) {
               complete.apply(this, arguments);
           }
       };

    });

    测试代码:
    for(var i = 0; i < 10; i++){

       var j = 0;
       $.ajax({
           url: 'http://js.passport.qihucdn.com/5.0.2.js',
           type: 'GET',
           dataType: 'HTML',
           complete: function(){
               console.log('complete:' + j++);
           }
       });

    }

阅读 1.8k

记录技术的点滴
记录一些自己正在学习的知识
6.1k 声望
103 粉丝
0 条评论
你知道吗?

6.1k 声望
103 粉丝
宣传栏