一 目录

不折腾的前端,和咸鱼有什么区别

目录
一 目录
二 前言
三 根本实现
 3.1 步骤一:前端局部
 3.2 步骤二:后端局部
 3.3 步骤三:前端局部
四 手写 JSONP
 4.1 封装 JSONP
 4.2 简略实现
 4.3 欠缺版本
五 参考文献

二 前言

返回目录

基本原理:利用 script 标签的 src 没有跨域限度来实现跨域目标。

执行过程

  1. 前端定义一个解析函数:jsonpCallBack = function (res) {}
  2. 通过 params 的模式包装 script 标签的申请参数,并且申明执行函数,如:cb = jsonpCallBack
  3. 后端获取前端申明的执行函数(jsonpCallBack),并且带上参数且调用函数的形式传递给前端
  4. 前端再 script 标签返回资源的时候就会执行 jsonpCallBack 并通过回调函数的形式拿到数据。

优缺点

  1. 【毛病】只能进行 GET 申请
  2. 【长处】兼容性好,在一些古老的浏览器中都能够运行。
注:本篇文章绝大部分参考 LinDaiDai_霖呆呆 大佬的手写内容,基于集体了解进行的再创作。呆呆大佬的文章在参考文献已给出

三 根本实现

返回目录

上面咱们通过 3 步骤来理解如何应用 JSONP

3.1 步骤一:前端局部

返回目录

前端代码:

index.html
<script type='text/javascript'>  window.jsonpCallBack = function(res) {    console.log(res);  }</script><script src='http://localhost:8080/api/jsonp?id=1&cb=jsonpCallBack' type='text/javascript'></script>
  1. 创立一个 jsonpCallBack 函数,然而还没有被调用。
  2. 加载 src 中的资源,调用 localhost:8080 端口的 API:api/jsonp,传递的参数是 id = 1 以及 cb = jsonpCallBack

3.2 步骤二:后端局部

返回目录

后端代码:

const Koa = require('koa');const app = new Koa();const items = [{ id: 1, title: 'title1' }, { id: 2, title: 'title2' }]app.use(async (ctx, next) => {  if (ctx.path === '/api/jsonp') {    const { cb, id } = ctx.query;    const title = items.find(item => item.id == id)['title']    ctx.body = `${cb}(${JSON.stringify({title})})`;    return;  }})console.log('listen 8080...')app.listen(8080);
  1. 解析 ctx.query,将 idcb 获取到。
  2. 查找 title
  3. 返回一个字符串,该字符串调用 cb 办法,并将 title 转成字符串,返回到内容 ctx.body 中。

3.3 步骤三:前端局部

返回目录

这时候前端接管到 Node.js 的返回,间接调用了 cb(title),办法,最终执行了它的回调,从而执行:

window.jsonpCallBack = function(res) {  console.log(res);}

输入:{ title: 'title1' }

四 手写 JSONP

返回目录

4.1 封装 JSONP

返回目录

定义一个 JSONP 办法,接管 4 个参数:

  • urlapi 接口
  • paramsapi 接口参数
  • callbackKey:与后端约定回调函数用哪个字段
  • callback:拿到数据之后前端执行的回调函数
JSONP({  url: 'https://www.baidu.com/s',  params: { wd: 'jsliang' },  callBackkey: 'cb',  callback(res) {    console.log(res);  }})

4.2 简略实现

返回目录

咱们看看简略实现:

const JSONP = ({  url,  params = {},  callBackkey = 'cb',  callback,}) => {  params[callBackkey] = callBackkey;  window[callBackkey] = callback;  const newParam = Object.keys(params).map((key) => `${key}=${params[key]}`).join('&');  const script = document.createElement('script');  script.setAttribute('src', `${url}?${newParam}`);  document.body.appendChild(script);}JSONP({  url: 'https://www.baidu.com/s',  params: { wd: 'jsliang' },  callBackkey: 'cb',  callback(res) {    console.log(res);  }})

4.3 欠缺版本

返回目录

优化屡次调用时候的一个问题:

function JSONP({  url,  params = {},  callbackKey = "cb",  callback,}) {  // 定义本地的惟一 callBackId,避免屡次调用的时候出问题  JSONP.callBackId = JSONP.callBackId || 1; // 默认为 1  // 拿到这个 id  const callBackId = JSONP.callBackId;  // 将要执行的回调如果到 JSONP 对象中,防止净化 window  JSONP.callbacks = JSONP.callbacks || [];  JSONP.callbacks[callBackId] = callback;  // 把这个名称退出到参数中:`cb = JSONP.callbacks[1]`  params[callbackKey] = `JSONP.callbacks[${callBackId}]`;  // 组合 params:'id=1&cb=JSONP.callbacks[1]'  const paramString = Object.keys(params).map((key) => `${key}=${params[key]}`);  // 动态创建 script 标签  const script = document.createElement("script");  script.setAttribute("src", `${url}?${paramString}`);  document.body.appendChild(script);  // id 自增,保障惟一  JSONP.callBackId++;}JSONP({  url: "http://localhost:8080/api/jsonp",  params: { id: 1 },  callbackKey: "cb",  callback(res) {    console.log(res);  },});JSONP({  url: "http://localhost:8080/api/jsonp",  params: { id: 2 },  callbackKey: "cb",  callback(res) {    console.log(res);  },});

以上,就是手写局部的内容。

五 参考文献

返回目录

对于本文影响较大的是呆呆大佬的文章:

  • [x] JSONP 原理及实现【浏览倡议:30min】

外面还有最终降级加强版,jsliang 就不颤抖了,面试问手写 JSONP 还没有碰到。


jsliang 的文档库由 梁峻荣 采纳 常识共享 署名-非商业性应用-雷同形式共享 4.0 国内 许可协定 进行许可。<br/>基于 https://github.com/LiangJunrong/document-library 上的作品创作。<br/>本许可协定受权之外的应用权限能够从 https://creativecommons.org/licenses/by-nc-sa/2.5/cn/ 处取得。