40行封裝一個jsonp包

第一次發文章,有點小緊張。es6

什麼是同源策略?json

爲何要有跨域限制?後端

爲何script標籤能跨域?跨域

這些基本的概念想必不用過多說明了,咱們切入正題:bash

在開發項目中若是有用到jsonp,一直使用的是一個jsonp包。使用過程當中參數須要格式化,也不曾進行Promise化,須要本身封裝。因而想一想能不能在包內就把這些事給辦了,之後調用就省心不少,因而點開jsonp包的源碼研讀一番,這裏簡單說明下jsonp的實現原理,簡單來講就是這樣的:app

1. 客戶端須要在本身這邊有一個定義好的接收服務端數據的函數函數

2. 使用script標籤發起一個服務端地址的請求jsonp

3. 服務端響應,對應的函數執行,傳參完成ui

這是最簡單的jsonp實現,這裏須要寫一個函數,須要寫一個script標籤,好像一點都不cool~url

接下來咱們手寫一個本身的jsonp,固然了,服務端怎麼知道你本地的準備函數是什麼,須要客戶端告訴它,並且服務端會根據客戶端提供的其餘參數動態生成數據給到咱們。因此咱們主要是實現如下幾個點:

1. 調用時動態建立script標籤

2. 註冊一個全局的方法等待被執行

3. 等待函數必定要在標籤發起請求以前準備好

4. 獲得數據以後移除建立的script標籤

5. 函數名不能重名,可能會同時發起多個請求

從上所述,在這裏咱們的方法須要如下幾個參數:

. url: 後端地址

. prefix: 執行函數名的前綴,後綴使用自增來確保惟一性

. param: 與後端協商好的發起jsonp請求時的字段

. timeout: 請求超時時間

. data: 服務端須要的其餘參數

jsonp實現跨域請求也有本身的侷限,如只能發起get請求。咱們這裏再原有的包上進行必定功能拓展,使用es6重構並將它Promise化,重構以後的代碼jsonp以下:

// 其餘參數在opts內
function jsonp(url, opts) {
    // 實現Promise化
    return new Promise((resolve, reject) => {
      // 自增值初始化 
      let count = 0;
      //設置默認參數
      const { 
        prefix = '__jp',
        param = 'callback',
        timeout = 60000,
        data = {}
      } = opts;
      let name = prefix + count++;
      let timer;
      //清除script標籤以及註冊的全局函數以及超時定時器
      function cleanup() { // 清除函數
        if (script.parentNode) {
          script.parentNode.removeChild(script);
          window[name] = null;
          if (timer) {
            clearTimeout(timer);
          }
        }
      }
      if (timeout) { // 超時
        timer = setTimeout(() => {
          cleanup();
          reject('timeout');
        }, timeout);
      }
      // 註冊全局函數,等待執行中...
      window[name] = res => {
        // 只要這個函數一執行,就表示請求成功,可使用清除函數了
        if (window[name]) {
          cleanup();
        }
        // 將請求到的數據扔給then
        resolve(res);
      }
      // 如下將data對象格式的參數拼接到url的後面
      let str = '';
      for (const key in data) {
        const value = data[key] !== undefined ? data[key] : '';
        str += `&${key}=${encodeURIComponent(value)}`;
      }
      url = url + (url.indexOf('?') > 0 ? '' : '?') + str.substr(1);
      // 最後加上與服務端協商的jsonp請求字段
      url = `${url}&${param}=${name}`;
      const script = document.createElement('script');
      script.src = url;
      // 如下這條執行且成功後,全局等待函數就會被執行
      document.head.appendChild(script);
    })
  }
複製代碼

# 大功告成,咱們請求一個QQ音樂的輪播圖數據試下:

const url = 'https://c.y.qq.com/musichall/fcgi-bin/fcg_yqqhomepagerecommend.fcg';
  const opts = {
    data: {
      g_tk: 1928093487,
      inCharset: 'utf-8',
      outCharset: 'utf-8',
      notice: 0,
      format: 'jsonp',
      platform: 'h5',
      uin: 0,
      needNewCode: 1
    },
    // QQ音樂接口Jsonp字段
    param: 'jsonpCallback'
  }
  
  jsonp(url, opts)
    .then(res => {
      console.log(res);
    })
    .catch(ex => {
      console.log(ex);
    })
複製代碼

數據請求成功!

相關文章
相關標籤/搜索