什么是JSONP

JSONP(JSON with Padding)是材料格局JSON的一种“应用模式”,能够让网页从别的网域获取材料。

因为浏览器同源策略,一般来说位于server1.a.com的网页无奈与 server2.a.com的服务器沟通,而HTML的 <script>元素是一个例外。利用 <script>元素的这个凋谢策略,网页能够失去从其余起源动静产生的JSON材料,而这种应用模式就是所谓的 JSONP。用JSONP抓到的数据并不是JSON,而是任意的JavaScript,用 JavaScript解释器执行而不是用JSON解析器解析。

JSONP实现原理

jsonp 概念咱们理解了,那么如何实现呢?

咱们要获取一段JSON数据没有跨域问题,咱们能够通过xhr GET 形式,假如申请地址是以后域下 /apis/data?id=123,以后域名是example.com;
返回数据是

{    name=peng,    age=18}

当咱们获取的数据不在同域的状况,比方上边的例子申请域名改成example2.com,页面所应用的域名还是example.com,应用 JSONP 的形式去跨域。

  1. 依据下面的原理简介,首先咱们在全局生命一个函数。

    window.jsonp1 = function(data) { console.log(data)}
  2. 动静的往head标签中插入script标签

    const head = document.getElementByTagName('head')[0]// 获取页面的head标签const script = document.createElement('script')// 创立script标签script.src = 'https://example2.com/apis/data?id=123&callback=jsonp1'// 给script标签赋值src地址head.appendChild(script)// 最初插入script标签
  3. 最初须要script标签返回的内容是一个办法调用并传入参数是要返回的内容。

    jsonp1({ name=peng, age=18})

以上就对JSONP原理进行一个繁难的实现。

真正实现一个JSONP网络申请库

以上对JSONP原理和实现有了初步理解,如果咱们要在日常我的项目中应用,那就须要会封装一个残缺的JSONP网络申请库。

上面咱们对jsonp-pro网络申请库做一个源码剖析,从中理解并学习如何封装一个JSONP网络申请库。

先来看看申请的通用办法 method 库

// 查看类型的办法,用于对办法传入类型的限度/** * object check method * * @param {*} item variable will be check * @param {string} type target type. Type value is 'String'|'Number'|'Boolean'|'Undefined'|'Null'|'Object'|'Function'|'Array'|'Date'|'RegExp' * @return {boolean} true mean pass, false not pass */function typeCheck(item, type) {  // 应用 Object.prototype.toString.call 办法,因为这个办法获取类型最全  const itemType = Object.prototype.toString.call(item);  // 拼接后果来做判断  let targetType = `[object ${type}]`;  if (itemType === targetType) {    return true;  } else {    return false;  }}// 获取随机数字型字符串,应用工夫戳+随机数拼接保障每次活的的字符串没有反复的function randNum() {  // get random number  const oT = new Date().getTime().toString();  const num = Math.ceil(Math.random() * 10000000000);  const randStr = num.toString();  return oT + randStr;}export { typeCheck, randNum };

主文件,次要办法

import { typeCheck, randNum } from './methods';// 传参的解释阐明,十分具体。这里不做过多解释/** * Param info * @param {string} url url path to get data, It support url include data. * @param {Object=} options all options look down * @param {(Object | string)=} options.data this data is data to send. If is Object, Object will become a string eg. "?key1=value1&key2=value2" . If is string, String will add to at the end of url string. * @param {Function=} options.success get data success callback function. * @param {Function=} options.error get data error callback function. * @param {Function=} options.loaded when data loaded callback function. * @param {string=} options.callback custom callback key string , default 'callback'. * @param {string=} options.callbackName callback value string. * @param {boolean} options.noCallback no callback key and value. If true no these params. Default false have these params * @param {string=} options.charset charset value set, Default not set any. * @param {number=} options.timeoutTime timeout time set. Unit ms. Default 60000 * @param {Function=} options.timeout timeout callback. When timeout run this function. * When you only set timeoutTime and not set timeout. Timeout methods is useless. */export default function(url, options) {  // 获取head节点,并创立scrpit节点  const oHead = document.querySelector('head'),    script = document.createElement('script');    // 申明变量,并给局部值增加默认值  let timer, // 用于工夫定时器    dataStr = '', // 用于存传输的query    callback = 'callback', // 和上边的参数一个含意    callbackName = `callback_${randNum()}`, // 和上边的参数一个含意    noCallback = false, // 和上边的参数一个含意    timeoutTime = 60000, // 和上边的参数一个含意    loaded, // 和上边的参数一个含意    success; // 和上边的参数一个含意  const endMethods = []; // 存储最初要执行回调函数队列  // 如果没有url参数抛出异样  if (!url) {    throw new ReferenceError('No url ! Url is necessary !');  }  // 对url参数进行类型查看  if (!typeCheck(url, 'String')) {    throw new TypeError('Url must be string !');  }  // 对所有参数进行解决的办法对象,命名与参数key放弃始终不便后续调用  const methods = {    data() {      // data 参数解决办法      const data = options.data;      if (typeCheck(data, 'Object')) {        // 如果是对象类型将对象转换成query字符串并赋值给下面申明过的变量        for (let item in data) {          dataStr += `${item}=${data[item]}&`;        }      } else if (typeCheck(data, 'String')) {        // 如果是字符串类型,间接赋值给上边变量        dataStr = data + '&';      } else {        // 其余状况抛出类型谬误        throw new TypeError('data must be object or string !');      }    },    success() {      // 对胜利参数办法进行解决      // 将胜利办法赋值给上边的变量      success = options.success;      // 进行类型查看,异样抛出谬误      if (!typeCheck(success, 'Function'))        throw new TypeError('param success must be function !');    },    error() {      // 对异样参数办法进行解决      // 进行类型查看,异样抛出谬误      if (!typeCheck(options.error, 'Function')) {        throw new TypeError('param success must be function !');      }      // 类型查看通过,script标签增加异样事件回调      script.addEventListener('error', options.error);    },    loaded() {      // 将加载实现办法进行解决      // 将加载实现办法赋值给上边变量      loaded = options.loaded;      // 进行类型查看,异样抛出谬误      if (!typeCheck(loaded, 'Function')) {        throw new TypeError('param loaded must be function !');      }    },    callback() {      // 将callback参数进行解决      callback = options.callback;      // 进行类型查看,异样抛出谬误      if (!typeCheck(callback, 'String')) {        throw new TypeError('param callback must be string !');      }    },    callbackName() {      // 将callbackName参数进行解决      callbackName = options.callbackName;      // 进行类型查看,异样抛出谬误      if (!typeCheck(callbackName, 'String')) {        throw new TypeError('param callbackName must be string !');      }    },    noCallback() {      // 将noCallback参数进行解决      noCallback = options.noCallback;      // 进行类型查看,异样抛出谬误      if (!typeCheck(noCallback, 'Boolean')) {        throw new TypeError('param noCallback must be boolean !');      }    },    charset() {      // 将charse参数进行解决      const charset = options.charset;      if (typeCheck(charset, 'String')) {        // 设置script标签charset,浏览器个别默认是UTF8,如果有非凡的须要手动设置        script.charset = charset;      } else {      // 进行类型查看,异样抛出谬误        throw new TypeError('param charset must be string !');      }    },    timeoutTime() {      // 将timeoutTime参数进行解决      timeoutTime = options.timeoutTime;      // 进行类型查看,异样抛出谬误      if (!typeCheck(timeoutTime, 'Number')) {        throw new TypeError('param timeoutTime must be number !');      }    },    timeout() {      // 将timeout办法进行解决       // 进行类型查看,异样抛出谬误      if (!typeCheck(options.timeout, 'Function')) {        throw new TypeError('param timeout must be function !');      }      function timeout() {        function outTime() {          // 移除无用的script节点          script.parentNode.removeChild(script);          // 删除命名在全局的办法          window.hasOwnProperty(callbackName) && delete window[callbackName];          // 革除定时器          clearTimeout(timer);          // 执行超时函数          options.timeout();        }                // 设置超时函数        timer = setTimeout(outTime, timeoutTime);      }      endMethods.push(timeout); // 超时函数放在队列中最初执行    }  };    // 遍历选项执行对应的办法  for (let item in options) {    methods[item]();  }  // 执行最初要执行的队列  endMethods.forEach(item => {    item();  });    // 如果没有回调,并且申请query不为空的状况。兼容是否有问号状况  // warn url include data  if (noCallback && dataStr != '') {    url.indexOf('?') == -1      ? (url += `?${dataStr.slice(0, -1)}`)      : (url += `&${dataStr.slice(0, -1)}`);  }  // 有回调且兼容有无问号状况  if (!noCallback) {    // 增加全局办法    window[callbackName] = data => {      // 有胜利回调则执行,并且将参数传入      success && success(data);      // 移除script标签      oHead.removeChild(script);      // 移除全局办法      delete window[callbackName];    };    url.indexOf('?') == -1      ? (url += `?${dataStr}${callback}=${callbackName}`)      : (url += `&${dataStr}${callback}=${callbackName}`);  }  // 对url编码  url = encodeURI(url);    // 给script标签增加加载实现回调  function loadLis() {    // 移除加载实现办法    script.removeEventListener('load', loadLis);    // 参数中有回调则执行回调    loaded && loaded();    // 革除定时器    clearTimeout(timer);  }    // 增加加载实现办法  script.addEventListener('load', loadLis);    // 将url赋值给script标签  script.src = url;  // 最初将script标签插入  oHead.appendChild(script);}

以上就实现实现了一个残缺的JSONP网络申请库。

jsonp-pro
github: https://github.com/peng/jsonp...
npm: https://www.npmjs.com/package...