• zepto源码研究


    简要:jsonp是一种服务器和客户端信息传递方式,一般是利用script元素赋值src来发起请求。一般凡是带有src属性的元素发起的请求都是可以跨域的。

    那么jsonp是如何获取服务器的数据的呢?

    jsonp先将指定的一个函数名作为url后面的参数传递到服务器,服务器取得函数名并将要传递的数据形成json格式与函数名包装起来形成脚本传递给客户端执行。

    /**
       * jsonp请求
       * @param options
       * @param deferred
       * @returns {*}
       */
      $.ajaxJSONP = function(options, deferred){
        //未设置type,就走     ajax     让参数初始化.
        //如直接调用ajaxJSONP,type未设置
        if (!('type' in options)) return $.ajax(options)
    
        var _callbackName = options.jsonpCallback,     //回调函数名
        //得到最终的回调函数名,callbackName为回调函数名称
        callbackName = ($.isFunction(_callbackName) ?
                _callbackName() : _callbackName) || ('jsonp' + (++jsonpID)), //没有回调,赋默认回调
        script = document.createElement('script'),
        originalCallback = window[callbackName], //回调函数
        responseData,
    
        //中断请求,抛出error事件
        //这里不一定能中断script的加载,但在下面阻止回调函数的执行
        abort = function(errorType) {
          $(script).triggerHandler('error', errorType || 'abort')
        },
        xhr = { abort: abort }, abortTimeout
    
        //xhr为只读deferred
        if (deferred) deferred.promise(xhr)
    
        //监听加载完,加载出错事件
        $(script).on('load error', function(e, errorType){
          //清除超时设置timeout
          clearTimeout(abortTimeout)
    
          //删除加载用的script。因为已加载完了,.off()清除掉绑定到dom的所有事件
          $(script).off().remove()
    
          //错误调用error
          if (e.type == 'error' || !responseData) {
            ajaxError(null, errorType || 'error', xhr, options, deferred)
          } else {
            //成功调用success
            ajaxSuccess(responseData[0], xhr, options, deferred)
          }
    
          //回调函数
          window[callbackName] = originalCallback
          if (responseData && $.isFunction(originalCallback))
            originalCallback(responseData[0])
    
          //清空闭包引用的变量值,不清空,需闭包释放,父函数才能释放。清空,父函数可以直接释放
          originalCallback = responseData = undefined
        })
    
        if (ajaxBeforeSend(xhr, options) === false) {
          abort('abort')
          return xhr
        }
    
    
        //回调函数设置,给后台执行此全局函数,数据塞入
        window[callbackName] = function(){
          responseData = arguments
        }
    
        //回调函数追加到请求地址
        script.src = options.url.replace(/?(.+)=?/, '?$1=' + callbackName)
        document.head.appendChild(script)
    
        //超时处理,通过setTimeout延时处理
        if (options.timeout > 0) abortTimeout = setTimeout(function(){
          abort('timeout')
        }, options.timeout)
    
        return xhr
      }

    $.ajaxJSONP(option,deffered) 这个方法在$.ajax中被调用,jsonp的请求和异步请求形式很像,但jsonp和ajax异步请求要严格去分开,jsonp是脚本加载形式。

    但在zepto里面,jsonp和ajax请求做了形式上的融合,都是调用$.ajax方法,那么在$.ajax里面针对jsonp请求做出了哪些处理呢?

     var dataType = settings.dataType, hasPlaceholder = /?.+=?/.test(settings.url);
        if (hasPlaceholder) dataType = 'jsonp'
    
        //不设置缓存,加时间戳 '_=' + Date.now()
        // 当settings.cache === null时
        if (settings.cache === false || (
                (!options || options.cache !== true) &&
                ('script' == dataType || 'jsonp' == dataType)
            ))
    
          //Date.now() == 1471504727756
          settings.url = appendQuery(settings.url, '_=' + Date.now())
    
        //如果是jsonp,调用$.ajaxJSONP,不走XHR,走script
        if ('jsonp' == dataType) {
          if (!hasPlaceholder)  //判断url是否有类似jsonp的参数
            settings.url = appendQuery(settings.url,
                settings.jsonp ? (settings.jsonp + '=?') : settings.jsonp === false ? '' : 'callback=?')
          return $.ajaxJSONP(settings, deferred)
        }

    1:首先判断settings.dataType ,如果是jsonp格式则在url后面添加settings.jsonp = ?,默认添加callback=?。然后再调用$.ajaxJSONP方法。但这里为什么不直接调用$.ajaxJSONP而多出一步呢?这里会校验url,如果url后面有callback=?的形式,则dataType强制为jsonp。其实这里将hasPlaceholder作为$.ajaxJSONP的第三个参数,然后将settings.url的参数的处理放在$.ajaxJSONP内部进行,也是可以的。但作者为了能使$.ajaxJSONP重用并符合语义化要求,也就默认认定传入其中的settings参数带有callback=?的形式。

    2:对于settings.url做appendQuery处理,其实就是保证settings.url后面一定得跟一个形式为callback=?的参数,如果之前url后面有,则不做处理。appendQuery是个工具,如果我们有在url后面加入参数的功能的时候,用它就可以了。

    3:调用$.ajaxJSONP ,

    流程总结如下:

    1:获取url参数中的回调函数名callbackName,若没有则默认一个。

    2:将callbackName函数对象保存起来。callbackName将在下面引用一个新的函数。

    3:创建一个新脚本元素命名为script。

    3:$(script).on("load error",function(){.....})

    4:判断ajaxBeforeSend,是否中断请求

    5:callbackName引用 function(){responseData = arguments},其作用是获取脚本里面的参数内容,然后以类似ajax的形式返回数据。

    6:将options.url最后的callback=?换成callback=callbackName,并赋值给script.src。

    7:设置超时处理

    $(script).on("load error",function(){.....})中大致内容如下:

    1:清除超时设置。2:off()清除掉script绑定的所有事件后清除掉remove()。

    3:若e.type == 'error' 或者 responseData为空,触发ajaxError,否则触发ajaxSuccess。

    4:将原来的callbackName传入responseData运行。

    5:很重要的一步,释放内存。

    以下是关于jsonp的服务器与客户端任务流程图


  • 相关阅读:
    flume-agent实例
    Apache Ant在Windows下配置环境变量
    作业流程和优化
    使用MapReduce查询Hbase表指定列簇的全部数据输出到HDFS(一)
    使用MapReduce将HDFS数据导入到HBase(二)
    数据结构(一)线性表——顺序表
    Java类集综合练习——信息管理(增、删、改、查)
    MySQL数据库表的数据插入、修改、删除、查询操作及实例应用
    总结一下这几节Java课的...重点!!!
    文件I/O操作——File类
  • 原文地址:https://www.cnblogs.com/zhutao/p/5841893.html
Copyright © 2020-2023  润新知