http://keeppassion.me/2021/02/04/HTTP004/
在上述博客中,已经讲解了同源政策与 JSONP,这边博客我们将对 JSONP 的代码进行优化,并封装成函数。
一、动态发送请求
我们先看一下之前的代码:
1 2 3 4 5 6 7 8
| <script> function fn (data) { console.log('客户端的fn函数被调用了') console.log(data); } </script>
<script src="http://localhost:3001/test"></script>
|
我们可以看出,请求在访问页面的时候就被立刻发送了。但是我们可能并不想请求立刻被发送,而是想发送时再发送,例如点击一个按钮时再发送,实现动态发送。
解决方案:
- 点击按钮后动态创建 script 标签,并设置 src 属性;
- 将 script 标签追加到页面中,请求是在 script 标签追加至页面之后发送的;
- 在 script 标签加载完毕后,将 script 标签标签移除。(避免点击一次就追加一个 script 标签,最后放进去了很多 script 标签,这样并不好)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <body> <button id="btn">点我发送请求</button> <script> function fn (data) { console.log('客户端的fn函数被调用了') console.log(data); } </script> <script type="text/javascript"> var btn = document.getElementById('btn'); btn.addEventListener('click', () => { var script = document.createElement('script'); script.src = 'http://localhost:3001/test'; document.body.appendChild(script); script.onload = ()=>{document.body.removeChild(script)}; }) </script> </body>
|
二、函数名称问题
上面我们只定义了一个函数 fn,但实际上会定义多个函数用于接收数据、处理数据。因此需要告知服务器端返回哪个函数的调用,故将函数名称作为参数传递过去。
1
| script.src = 'http://localhost:3001/test?callback=fn';
|
三、封装 jsonp 函数
现在我们对 jsonp 进行封装。
1 2 3 4 5 6
| function jsonp(options) { var script = document.createElement('script'); script.src = options.url; document.body.appendChild(script); script.onload = ()=>{document.body.removeChild(script)}; }
|
再调用一下。
1 2 3 4
| var btn = document.getElementById('btn'); btn.addEventListener('click', () => { jsonp({ url: 'http://localhost:3001/test?callback=fn' }) })
|
四、优化 jsonp 函数
我们上面的做法存在3个问题:
- 问题1:我们定义了 jsonp 函数用于发送请求,然后还需要 fn 函数来接收数据、处理数据,以至于发送一个请求需要使用两个函数。而且两个函数是独立的,破坏了封装性。因此我们希望将接收数据的函数写进 jsonp 函数的实参里,作为回调函数使用。
- 问题2:我们可能需要发送多个请求,每个请求都需要一个回调函数来处理服务器响应的结果,取名字也是一件头疼的事情。因此我们可以让函数的名字随机产生。
- 问题3:我们希望 jsonp 函数能够传递参数。
改进后的 jsonp 函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| function jsonp(options) { let script = document.createElement('script');
let params = ''; for(let key in options.data) { params += `&${key}=${options.data[key]}` }
let fnName = 'myJsonp' + Math.random().toString().replace('.','');
window[fnName] = options.success;
script.src = options.url + '?callback=' + fnName + params; document.body.appendChild(script); script.onload = ()=>{document.body.removeChild(script)}; }
|
我们访问腾讯天气测试一下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| var btn = document.getElementById('btn'); btn.addEventListener('click', () => { jsonp({ url: 'https://wis.qq.com/weather/common', data: { source: 'pc', weather_type: 'forecast_24h', province: '陕西省', city: '西安市' }, success(data) { console.log(data.data); } }) })
|
可以成功获取天气数据: