jsonp封装方法二

本文介绍了一种基于JSONM协议的跨域数据加载方法,利用动态加载script和执行callback实现跨域通信,适用于静态数据存储在CDN场景。探讨了在IE6-8浏览器中的限制及解决方案,并分享了实现细节与调试经验。

前言: 看到玉伯的聊聊jsonp的p,引发了另一种loader方式来跨域的方法,他把它叫做JSONM协议,原理和seajs相似,都是动态加载script,加载完成后执行callback,

同时还不用考虑回调函数名,都指定为define,服务端可以静态存储例如(define({name:"alice",age:21}))。如果数据是静态的,还可以放在cdn上。原文issue连接

以及一篇原理分析,写得很赞 连接

 

所以我就斗胆按照自己的理解,自己写了一个简易的用loader的思想来完成jsonp。

原理概述:动态加载script,利用onload事件执行回调函数,在ie9/10中 用onreadystatechange。可是在ie6-8中不支持,在seajs里看到了绝招,利用readyState为interactive判断正在执行的脚本,脚本的src和传入时的callback建立唯一连接,进行查找,执行回调函数。

那么怎么获取json数据呢? 我用JSONPData这个里存储获取到的_jsondata(私有变量,提供接口访问)。在非ie中就简单了,直接callback.call(this,JSONPData.getJsonData()).

 

我实现的这个有什么限制呢? 

在ie6-8中,因为用的是src和callback唯一对应,假若几个请求的src相同,callback只能得到第一次设置的函数,解决方法,如果回调函数不同,就用不同的src请求喽,加个无意义的参数就ok啦。

 

 

 var currentAddingScript;
 var JSONP = (function() {
     var _head = document.getElementsByTagName("head")[0];
     var _baseElement = document.getElementsByTagName("base")[0];
     var _helper = {
         encodeURL: function(url) {
             var param = url.split("?")[1];
             var url = url.split("?")[0];
             var params = param.split("&");
             var parLen = params.length;

             for (var i = 0; i < parLen; i++) {
                 var paItem = params[i].split("=");
                 url += url.indexOf("?") >= 0 ? "&" : "?";
                 url += encodeURIComponent(paItem[0]) + "=" + encodeURIComponent(paItem[1]);
             }
             return url;
         },
         addLoad: function(node, callback) {
             node.onload = node.readystatechange = function() {
                 // Ensure only run once  in IE9
                 node.onload = node.readystatechange = null;
                 if (!this.readyState || node.readyState == "complete" || node.readyState == "loaded") {
                     callback.call(this, JSONPData.getJsonData());
                     this.parentNode.removeChild(this);
                 }
             }
         }
     };
     return {
         start: function(url, func) {
             var encUrl = url;//存储的是未经过编码的,以便与对应的callback创建关联
             //创建script结点
             var script = document.createElement("script");
             if (encUrl.indexOf("?") >= 0) {
                 url = encUrl + "&callback=JSONP.callback";
                 script.src = _helper.encodeURL(url);
             } else {
                 url = encUrl + "?callback=JSONP.callback";
                 script.src = _helper.encodeURL(url);
             }
             script.charset = "UTF-8";

             //绑定加载完成事件
             _helper.addLoad(script, func);

             // For some cache cases in IE 6-8, the script executes IMMEDIATELY after
             // the end of the insert execution, so use `currentlyAddingScript` to
             // hold current node,
             currentAddingScript = script;
             _baseElement ? _head.insertBefore(script, _baseElement) : _head.appendChild(script);

             //for ie6-9 关联script 和callback           
             var data = {};
             data[url] = func;
             JSONPData.save(data);
         },
         getInteractiveScript: function() {
             //ie6-9  获取正在执行的脚本

             if (currentAddingScript) {
                 return currentAddingScript;
             }
             var scripts = document.getElementsByTagName('script');
             for (var i = 0; i < scripts.length; i++) {
                 var script = scripts[i];
                 if (script.readyState === 'interactive') {
                     return script;
                 }
             }
             return null;
         },
         callback: function(data) {

             // if ie6-9
             if (document.attachEvent) {
                 var script = JSONP.getInteractiveScript();
                 var callback = null;
                 if (script) {
                     callback = JSONPData.findCallback(script.src);
                     if (typeof callback == "function") {
                         callback(data);
                     }
                 } else {
                     console.log("fail");
                 }
             }
             // 设置返回的json数据,供调用
             JSONPData.setJsonData(data);
         }
     }
 })();

 var JSONPData = (function() {
     var _dataStorage = [];
     var _jsonData;

     // 每个数据项是data {url: [callbackFunc]}
     return {
         save: function(data) {
             //检查data的url若相同,则回调函数不变
             for (var i in data) {
                 if (!_dataStorage.hasOwnProperty(i)) {
                     _dataStorage.push(data);
                 }
             }
         },
         findCallback: function(url) {
             var len = _dataStorage.length;
             for (var i = 0; i < len; i++) {
                 if (_dataStorage[i][url]) {
                     return _dataStorage[i][url];
                 }
             }
             return null;
         },
         setJsonData: function(data) {
             _jsonData = data ? data : null;
         },
         getJsonData: function() {
             return _jsonData;
         }
     }
 })();

 update分割线

==========================================================================================

我用Ie的仿真模式,测试ie7和ie8时,发现第一次点击按钮触发请求不能返回数据,请先看重要的html部分

<button id="button">发送三个jsonp请求</button>

绑定button点击事件

   var button = document.getElementById("button");
    button.onclick = function() {
        JSONP.start("http://localhost:8088/data.php", function(data) {
            console.log(data);
            console.log("yes1");
        });
        JSONP.start("http://localhost:8088/data.php?cc=ff", function(data) {
            console.log(data);
            console.log("yes2");
        });
        JSONP.start("http://localhost:8088/data.php?ur=f", function(data) {
            console.log(data);
            console.log("yes3");
        });
    }

在ie7,8下我点一下后不出现结果,

 

通过断点调试,代码问题出在了这段上,我先把创建的script结点添加到dom结点中,之后再存储url和callback,导致在立即执行函数时,数据还未存储,无法找到关联的callback,所以没有成功调用,解决方案是,存储数据放在插入节点之前,就完美啦!

 再看运行结果:

 

新代码:就只贴了需要做小改动得JSONP返回的start函数代码:

 start: function(url, func) {
                var encUrl = url;

                //创建script结点
                var script = document.createElement("script");
                if (encUrl.indexOf("?") >= 0) {
                    url = encUrl + "&callback=JSONP.callback";
                    script.src = _helper.encodeURL(url);
                } else {
                    url = encUrl + "?callback=JSONP.callback";
                    script.src = _helper.encodeURL(url);
                }
                script.charset = "UTF-8";

                //for ie6-9 关联script 和callback     
                var data = {};
                data[url] = func;
                JSONPData.save(data);
                 
                //绑定加载完成事件
                _helper.addLoad(script, func);

                // For some cache cases in IE 6-8, the script executes IMMEDIATELY after
                // the end of the insert execution, so use `currentlyAddingScript` to
                // hold current node,
                currentAddingScript = script;
                _baseElement ? _head.insertBefore(script, _baseElement) : _head.appendChild(script);     
            }, 

  

 

转载于:https://www.cnblogs.com/AliceX-J/p/5896646.html

axios本身不提供对JSONP的直接支持,但是您可以通过一些封装来实现使用axios发送JSONP请求。下面是一个简单的axios.jsonp封装的示例: ```javascript import axios from 'axios'; function jsonp(url, params) { return new Promise((resolve, reject) => { const callbackName = `jsonpCallback_${Date.now()}`; // 创建一个全局函数,用于处理JSONP响应 window[callbackName] = function(data) { // 在这里处理响应数据 resolve(data); cleanup(); }; // 清理回调函数 function cleanup() { delete window[callbackName]; script.parentNode.removeChild(script); } // 创建一个<script>标签并添加到页面上 const script = document.createElement('script'); script.src = `${url}?${serializeParams(params)}&callback=${callbackName}`; script.onerror = function() { reject(new Error('JSONP request failed')); cleanup(); }; document.body.appendChild(script); }); } // 辅助函数,将参数对象序列化为URL查询字符串 function serializeParams(params) { return Object.keys(params) .map(key => `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`) .join('&'); } export default jsonp; ``` 使用这个封装的axios.jsonp函数,您可以像这样发送JSONP请求: ```javascript import jsonp from './jsonp'; jsonp('http://example.com/api', { param1: 'value1', param2: 'value2' }) .then(response => { // 处理响应数据 console.log(response); }) .catch(error => { // 处理错误 console.error(error); }); ``` 这个封装中,我们创建了一个全局唯一的回调函数名称,并将其作为参数传递给服务器。然后,我们创建一个全局函数来处理JSONP响应,并在Promise中进行resolve或reject。最后,我们创建一个`<script>`标签并将其添加到页面上,然后等待JSONP响应。 请注意,使用JSONP有一些安全风险,并且可能不适用于所有情况。在使用JSONP时,请确保您信任提供JSONP服务的服务器,并了解潜在的安全风险。 希望这可以帮助您实现使用axios发送JSONP请求!如有任何进一步的问题,请随时提问。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值