zepto源碼研究 - ajax.js($.ajaxJSONP 的分析)


簡要: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的服務器與客戶端任務流程圖


 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM