JSONP 是服务器与客户端跨源通信的常用方法,最大特点就是简单适用,老式浏览器全部支持,服务器改造非常小。

它的基本思想是,网页通过添加一个 script 元素,向服务器请求 JSON 数据,这种做法不受同源政策限制;服务器收到请求后,将数据放在一个指定名字的回调函数里传回来。

缺点

  • 只能发送 get 请求,不支持 post put delete
  • 不安全,xss 攻击,因此很多公司现在都不采用

实现步骤

(1)网页动态插入 script 元素,由它向跨源网址发出请求。

  1. function addScriptTag(src) {
  2. var script = document.createElement('script');
  3. script.setAttribute('type','text/javascript');
  4. script.src = src;
  5. document.body.appendChild(script);
  6. }
  7. window.onload = function() {
  8. addScriptTag('http://example.com/ip?callback=foo');
  9. }
  10. function foo(data) {
  11. console.log('Your public IP address is:' + data.ip);
  12. }

通过动态添加 script 元素,向服务器 example.com 发出请求。

(2)该请求的查询字符串有一个 callback 参数,用来指定回调函数的名字,这对于 JSONP 是必需的。

(3)服务器收到这个请求以后,会将数据放在回调函数的参数位置返回。

  1. foo({
  2. "ip": "8.8.8.8"
  3. });

由于 script 元素请求的脚本,直接作为代码运行。这时,只要浏览器定义了 foo 函数,该函数就会立即调用。作为参数的 JSON 数据被视为 JavaScript 对象,而不是字符串,因此避免了使用 JSON.parse 的步骤。

封装 JSONP 方法

通过 Promise 来管理这个操作。

参数

  • url:请求地址
  • params:传递给后台的参数
  • cb:回调函数名称

代码

  1. //=> 解构赋值
  2. function jsonp({url, params, cb}) {
  3. return new Promise((resolve, reject) => {
  4. // 手动创建一个 script,插入到 body 后面
  5. let script = document.createElement('script');
  6. // 创建一个全局 cb 回调函数
  7. // 数据请求回来之后,会执行这个函数,触发 resolve,完成使命,移除这个 script 标签
  8. // 继而执行 then 中定义的回调函数
  9. window[cb] = function (data) {
  10. resolve(data);
  11. document.body.removeChild(script);
  12. }
  13. // 拼接参数
  14. params = {...params, cb};
  15. let arr = [];
  16. for (let k in params) {
  17. arr.push(`${k}=${params[k]}`);
  18. }
  19. script.src = `${url}?${arr.join('&')}`;
  20. document.body.appendChild(script);
  21. })
  22. }

后端服务

使用 express 框架

  1. let express = require('express');
  2. let app = express();
  3. app.get('/say', function (req, res) {
  4. let { wd, cb } = req.query;
  5. console.log(wd);
  6. res.end(`${cb}('中文')`);
  7. })
  8. app.listen(3000);