美文网首页
jsonp原理和实现

jsonp原理和实现

作者: 高级程序狗 | 来源:发表于2020-01-27 16:38 被阅读0次

    是时候彻底搞清jsonp了!

    jsonp主要是利用了脚本不受 同源策略 限制的"BUG",算是一种简单的hack写法,主要分3个步骤:

    1.前端请求一个脚本,注意这里是 脚本 而不是ajax。通常是动态创建一个script标签,文件的地址就是异步地址,但是会带一个callback参数(也可以和后端商量约定)https://xxx.com/api/getsth?callback=__jsonp1234,callback参数的值是唯一的,用来区分不同的请求。

      //ps:如果不需要接收response,比如埋点统计,甚至可以创建一个image
      var target = document.getElementsByTagName('script')[0] || document.head;
      var script;
      script = document.createElement('script');
      script.src = url;//这里的url实际就是异步地址
      target.parentNode.insertBefore(script, target);
    

    2.后端需要配合前端做下兼容:

    //假设要返回的数据是:{name:'wyz',age:18}
    var data = {name:'wyz',age:18};
    
    if(req.query.callback){
        //当发现前端传了callback参数
        //实际输出:__jsonp1234({name:'wyz',age:18})
        res.send(`${req.query.callback}(${data})`)
    }else{
        //没传callback正常返回即可
        res.json(data)
    }
    

    3.页面加载完成这个脚本后,相当于在创建script标签的位置调用了__jsonp1234({name:'wyz',age:18})方法。如果在请求之前,已经创建好了全局方法window.__jsonp1234 = function(data){console.log(data)}呢?

    <html>
    <head>
    </head>
    <body>
        <!--...-->
    
    
        <!--这个是动态创建的-->
        <script src="https://xxx.com/api/getsth?callback=__jsonp1234"></script>
        <!--加载完成后,相当于:-->
        <!-- <script>
            __jsonp1234({name:'wyz',age:18})
        </script> -->
    
        <script>
            var target = document.getElementsByTagName('script')[0] || document.head;
            var script;
            script = document.createElement('script');
            script.src = "https://xxx.com/api/getsth?callback=__jsonp1234";
            target.parentNode.insertBefore(script, target);
            window.__jsonp1234 = function (data) {
                console.log(data)//当请求完成,调用了__jsonp1234方法,这里就会打印出来
            }
        </script>
    </body>
    </html>
    

    是不是so easy?趁热打铁,附上一个jsonp的实现源码:

    /**
     * Module dependencies
     */
    
    var debug = require('debug')('jsonp');//这个debug插件晚点有空研究一下
    
    /**
     * Module exports.
     */
    
    module.exports = jsonp;
    
    /**
     * Callback index.
     */
    
    var count = 0;
    
    /**
     * Noop function.
     */
    
    function noop(){}
    
    /**
     * JSONP handler
     *
     * Options:
     *  - param {String} qs parameter (`callback`)
     *  - prefix {String} qs parameter (`__jp`)
     *  - name {String} qs parameter (`prefix` + incr)
     *  - timeout {Number} how long after a timeout error is emitted (`60000`)
     *
     * @param {String} url
     * @param {Object|Function} optional options / callback
     * @param {Function} optional callback
     */
    
    function jsonp(url, opts, fn){
      if ('function' == typeof opts) {
        fn = opts;
        opts = {};
      }
      if (!opts) opts = {};
    
      var prefix = opts.prefix || '__jp';
    
      // use the callback name that was passed if one was provided.
      // otherwise generate a unique name by incrementing our counter.
      var id = opts.name || (prefix + (count++));
    
      var param = opts.param || 'callback';
      var timeout = null != opts.timeout ? opts.timeout : 60000;
      var enc = encodeURIComponent;
      var target = document.getElementsByTagName('script')[0] || document.head;
      var script;
      var timer;
    
    
      if (timeout) {
        timer = setTimeout(function(){
          cleanup();
          if (fn) fn(new Error('Timeout'));
        }, timeout);
      }
    
      function cleanup(){
        if (script.parentNode) script.parentNode.removeChild(script);
        window[id] = noop;
        if (timer) clearTimeout(timer);
      }
    
      function cancel(){
        if (window[id]) {
          cleanup();
        }
      }
    
      window[id] = function(data){
        debug('jsonp got', data);
        cleanup();
        if (fn) fn(null, data);
      };
    
      // add qs component
      url += (~url.indexOf('?') ? '&' : '?') + param + '=' + enc(id);
      url = url.replace('?&', '?');
    
      debug('jsonp req "%s"', url);
    
      // create script
      script = document.createElement('script');
      script.src = url;
      target.parentNode.insertBefore(script, target);
    
      return cancel;
    }
    
    

    相关文章

      网友评论

          本文标题:jsonp原理和实现

          本文链接:https://www.haomeiwen.com/subject/uzdethtx.html