背景
As we all know,浏览器同源策略会将非同源请求(跨域)抛弃,而许多时候我们可能併未将前端资源与服务端服务放在一个服务器,此时就需要一个跨域的手段了。
基于此,针对同源策略,衍生了一种跨域的方法Jsonp。
Jsonp简单讲,即利用了script标签不受浏览器同源策略影响的特性,从而利用script想服务端跨域请求的方式。
原理解析
封装一个Jsonp函数,Promise化
// jsonp.tstype JsonpOptions<T extends Record<string, any>> = { url: string, timeout: number, data: T, cbKey?: string};interface JsonpRes { code: 0 | -1, data: any, msg: string}export default function createJsonp<T extends Record<string, any>> (options: JsonpOptions<T>): Promise<JsonpRes> { return new Promise((resolve, reject) => { // 第一次请求,初始化随机数 if (!jsonpNumber) { jsonpNumber = 1; } else { jsonpNumber++; // 拼接到callbackFn上,以防止多个jsonp请求时错误使用同一个callback } // callback接收函数名 const jsonpReceiverFnName = 'jsonpReceiver' + jsonpNumber; const { url, timeout, data, cbKey = 'callback' } = options; let script: HTMLScriptElement; // 将被插入DOM的script标签 // 设置超时定时器 let timeoutTimer = setTimeout(() => { reject({ code: -1, data: null, msg: '请求超时' }); clear(); }, timeout); // 清除上一次请求存的变量/定时器 function clear () { window[jsonpReceiverFnName] = null; // 删除callbackFn clearTimeout(timeoutTimer); // 清除定时器 script.parentNode?.removeChild(script); // 移除当前jsonp的script } window[jsonpReceiverFnName] = (res) => { resolve({ code: 0, data: res, msg: '请求成功' }); // 将数据返回到 clear(); // 请求成功后,清理本次jsonp带来的“副作用” } // 创建JSONP请求标签,发出请求 let params = ''; // 仅支持get请求,故只有query参数 // 遍歴data,拼接参数 for (let key in data) { if (data.hasOwnProperty(key)) { params += `&${key}=${data[key]}`; } } params = params.substring(1); // 移除第一个 & script = document.createElement('script'); script.src = `${url}?${params}&${cbKey}=${jsonpReceiverFnName}`; // 拼接请求url document.body.appendChild(script); // 插入body,正式发出jsonp请求 });};
调用
// 调用jsonp函数<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <script src="./jsonp.js"></script></head><body> <script> async function testJsonp() { try { const res = await createJsonp({ url: 'https://x.x.x.x/testjsonp', timeout: 15000, data: { id: '00145x', level: 2 } }); !res.code && console.log(res.data) } catch (rejectRes) { rejectRes.code && console.log(rejectRes?.msg) } } testJsonp(); </script></body></html>