【JS】「一起造轮子」从1.9k的jsonp库出发🏗一起实现jsonp

「一起造轮子」从1.9k的jsonp库出发🏗一起实现jsonp

修仙大橙子发布于 今天 02:51

1. 什么是jsonp?

下方是维基百科对JSON的解释
【JS】「一起造轮子」从1.9k的jsonp库出发🏗一起实现jsonp

从这个解释中,我们可以知道,完成jsonp需要的步骤主要有以下两点:

  1. 向页面中插入一个带有请求链接的<script>标签
  2. 通过回调函数,获取需要的JSON数据

2. jsonp库是如何实现的?

jsonp是一个star数1.9k的仓库,实现了一个简单的jsonp方法

jsonp仓库传送门

2.1 传入参数

  • url

传入的url就是需要请求的链接地址

  • opts

param:传入的是缀在链接后的参数,默认为callback

timeout:请求超时时间,默认为60000

prefix:全局回调函数名称的前缀,默认为__jp

name:全局回调函数的名字,默认由前缀和自增数字生成

  • fn

回调函数的第一个参数是err,如果失败返回错误:Timeout,如果成功返回null。
第二个参数是data,也就是最终请求的内容

调用该函数时,还会返回一个取消函数,如果希望取消请求,直接调用返回方法即可。

2.2 分析代码

2.2.1 定义变量

【JS】「一起造轮子」从1.9k的jsonp库出发🏗一起实现jsonp

count为计数器,noop为空函数(后面在重置全局函数时会用到)。


【JS】「一起造轮子」从1.9k的jsonp库出发🏗一起实现jsonp

将2.1中定义的默认值,在代码里初始化,并且定义了变量。

2.2.2 设置超时定时器 & 清理页面中的代码

【JS】「一起造轮子」从1.9k的jsonp库出发🏗一起实现jsonp

将页面插入的<script>标签代码删除,并将全局的回调方法置为空方法。如果有定时器则删除定时器


【JS】「一起造轮子」从1.9k的jsonp库出发🏗一起实现jsonp

调用超时后,清除清除页面中的代码。如果有回调函数,将会抛出Timeout报错。


【JS】「一起造轮子」从1.9k的jsonp库出发🏗一起实现jsonp

定义了返回的取消函数,本质上是调用cleanup函数清理全局页面中的代码。

2.2.3 将回调函数挂载到全局

【JS】「一起造轮子」从1.9k的jsonp库出发🏗一起实现jsonp

将回调函数挂载到全局,返回数据后调用cleanup函数清理全局页面中的代码,并将数据返回给传入的fn函数

2.2.4 处理请求地址

【JS】「一起造轮子」从1.9k的jsonp库出发🏗一起实现jsonp

处理请求地址,将encodeURIComponent后的参数拼接至url

2.2.5 挂载<script>并返回取消函数

【JS】「一起造轮子」从1.9k的jsonp库出发🏗一起实现jsonp

创建<script>标签,并挂载到页面上。最后返回取消函数。

使用target.parentNode.insertBefore的原因是由于target.appendChild兼容性不佳。按照提交者的说法是:

3.如何实现一个自己的jsonp?

通过分析上面的代码,我们不难发现,主要是完成以下几个功能

  1. 实现请求超时报错
  2. 实现将回调函数挂载至window
  3. 实现处理url请求
  4. 实现创建script标签,并插入页面中

第一、四部分的代码,我们可以继续使用。

第二部分的代码,实际上还是无法保证回调函数的名称不与全局的方法冲突,因此需要生成一个唯一的函数名称,如果检查名称有冲突则知道生成一个唯一的名称为止。

第三部分的代码,在处理的请求中,传入的参数用的是string,但是平时开发常用的多为对象,因此在这里需要支持传入对象后并处理成字符串。

3.1 生成唯一函数名代码

function getRandomKey(length = 6) {

let randomKey = '';

for (let i = 0; i < length; i++) {

// 生成0~9和a-z的随机字符串

randomKey += ((Math.random() * 36) | 0).toString(36);

}

return randomKey;

}

function checkRandomKey(key, obj) {

// 检查当前生成的key值是否已经存在于obj中

return obj[key] === undefined

? key

: checkRandomKey(getRandomKey(), obj);

}

checkRandomKey(getRandomKey(), window);

将会在window上检测生成的随机字符串是否已被占用,如果被占用,则再生成一个。

3.2 拼接对象类型的参数

for (var key in params) {

param += `${key}=${encodeURIComponent(params[key])}&`;

}

将代码拼接成字符串,并且使用encodeURIComponent进行转义。

3.3 优化传入参数

url参数并入opts中,并将opts改名为config(比较喜欢axios的设计,所以叫了一样的名字😁),fn修改为callback

4. 最终代码

function jsonp(config, callback) {

let {url, params, name, prefix = '_jsonp_callback_', timeout = 60000} = config;

const target = document.getElementsByTagName('script')[0] || document.head;

let script;

let timer;

let callbackFunctionName;

let paramsString = '';

// 定义空函数

function noop() {

}

// 生成随机key值

function getRandomKey(length = 6) {

let randomKey = '';

for (let i = 0; i < length; i++) {

// 生成0~9和a-z的随机字符串

randomKey += ((Math.random() * 36) | 0).toString(36);

}

return randomKey;

}

function checkRandomKey(key, obj) {

// 检查当前生成的key值是否已经存在于obj中

return obj[key] === undefined

? key

: checkRandomKey(getRandomKey(), obj);

}

// 确定挂在window上的回调函数名称

callbackFunctionName = name || checkRandomKey(getRandomKey(), window);

// 清理不需要的代码

function cleanup() {

if (script.parentNode) script.parentNode.removeChild(script);

window[callbackFunctionName] = noop;

if (timer) clearTimeout(timer);

}

// 取消调用

function cancel() {

if (window[callbackFunctionName]) cleanup();

}

// 设置定时器

if (timeout) {

timer = setTimeout(function () {

cleanup();

if (callback) callback(new Error('Timeout'));

}, timeout);

}

// 将传入的params转化为字符串

if (params) {

for (var key in params) {

paramsString += `${key}=${encodeURIComponent(params[key])}&`;

}

}

// 拼接默认的callback内容

paramsString += `callback=${prefix}${callbackFunctionName}`;

// 将回调函数设置到window上

window[callbackFunctionName] = function (data) {

cleanup();

if (callback) callback(null, data);

};

// 将请求参数拼接至url上

url += (~url.indexOf('?') ? '&' : '?') + paramsString;

url = url.replace('?&', '?');

// 创建一个script标签并插入到页面中

script = document.createElement('script');

script.src = url;

target.parentNode.insertBefore(script, target);

// 返回取消函数

return cancel;

}

至此我们完成了我们自己的jsonp轮子。如果发现有问题,欢迎评论区留言。

5. 参考资料

  • github-jsonp
  • 手动实现JSONP

javascript前端jsonp一起写轮子

阅读 29发布于 今天 02:51

本作品系原创,采用《署名-非商业性使用-禁止演绎 4.0 国际》许可协议


前端小站

前端的内容小记

avatar

修仙大橙子

前端工程师

83 声望

3 粉丝

0 条评论

得票时间

avatar

修仙大橙子

前端工程师

83 声望

3 粉丝

宣传栏

1. 什么是jsonp?

下方是维基百科对JSON的解释
【JS】「一起造轮子」从1.9k的jsonp库出发🏗一起实现jsonp

从这个解释中,我们可以知道,完成jsonp需要的步骤主要有以下两点:

  1. 向页面中插入一个带有请求链接的<script>标签
  2. 通过回调函数,获取需要的JSON数据

2. jsonp库是如何实现的?

jsonp是一个star数1.9k的仓库,实现了一个简单的jsonp方法

jsonp仓库传送门

2.1 传入参数

  • url

传入的url就是需要请求的链接地址

  • opts

param:传入的是缀在链接后的参数,默认为callback

timeout:请求超时时间,默认为60000

prefix:全局回调函数名称的前缀,默认为__jp

name:全局回调函数的名字,默认由前缀和自增数字生成

  • fn

回调函数的第一个参数是err,如果失败返回错误:Timeout,如果成功返回null。
第二个参数是data,也就是最终请求的内容

调用该函数时,还会返回一个取消函数,如果希望取消请求,直接调用返回方法即可。

2.2 分析代码

2.2.1 定义变量

【JS】「一起造轮子」从1.9k的jsonp库出发🏗一起实现jsonp

count为计数器,noop为空函数(后面在重置全局函数时会用到)。


【JS】「一起造轮子」从1.9k的jsonp库出发🏗一起实现jsonp

将2.1中定义的默认值,在代码里初始化,并且定义了变量。

2.2.2 设置超时定时器 & 清理页面中的代码

【JS】「一起造轮子」从1.9k的jsonp库出发🏗一起实现jsonp

将页面插入的<script>标签代码删除,并将全局的回调方法置为空方法。如果有定时器则删除定时器


【JS】「一起造轮子」从1.9k的jsonp库出发🏗一起实现jsonp

调用超时后,清除清除页面中的代码。如果有回调函数,将会抛出Timeout报错。


【JS】「一起造轮子」从1.9k的jsonp库出发🏗一起实现jsonp

定义了返回的取消函数,本质上是调用cleanup函数清理全局页面中的代码。

2.2.3 将回调函数挂载到全局

【JS】「一起造轮子」从1.9k的jsonp库出发🏗一起实现jsonp

将回调函数挂载到全局,返回数据后调用cleanup函数清理全局页面中的代码,并将数据返回给传入的fn函数

2.2.4 处理请求地址

【JS】「一起造轮子」从1.9k的jsonp库出发🏗一起实现jsonp

处理请求地址,将encodeURIComponent后的参数拼接至url

2.2.5 挂载<script>并返回取消函数

【JS】「一起造轮子」从1.9k的jsonp库出发🏗一起实现jsonp

创建<script>标签,并挂载到页面上。最后返回取消函数。

使用target.parentNode.insertBefore的原因是由于target.appendChild兼容性不佳。按照提交者的说法是:

3.如何实现一个自己的jsonp?

通过分析上面的代码,我们不难发现,主要是完成以下几个功能

  1. 实现请求超时报错
  2. 实现将回调函数挂载至window
  3. 实现处理url请求
  4. 实现创建script标签,并插入页面中

第一、四部分的代码,我们可以继续使用。

第二部分的代码,实际上还是无法保证回调函数的名称不与全局的方法冲突,因此需要生成一个唯一的函数名称,如果检查名称有冲突则知道生成一个唯一的名称为止。

第三部分的代码,在处理的请求中,传入的参数用的是string,但是平时开发常用的多为对象,因此在这里需要支持传入对象后并处理成字符串。

3.1 生成唯一函数名代码

function getRandomKey(length = 6) {

let randomKey = '';

for (let i = 0; i < length; i++) {

// 生成0~9和a-z的随机字符串

randomKey += ((Math.random() * 36) | 0).toString(36);

}

return randomKey;

}

function checkRandomKey(key, obj) {

// 检查当前生成的key值是否已经存在于obj中

return obj[key] === undefined

? key

: checkRandomKey(getRandomKey(), obj);

}

checkRandomKey(getRandomKey(), window);

将会在window上检测生成的随机字符串是否已被占用,如果被占用,则再生成一个。

3.2 拼接对象类型的参数

for (var key in params) {

param += `${key}=${encodeURIComponent(params[key])}&`;

}

将代码拼接成字符串,并且使用encodeURIComponent进行转义。

3.3 优化传入参数

url参数并入opts中,并将opts改名为config(比较喜欢axios的设计,所以叫了一样的名字😁),fn修改为callback

4. 最终代码

function jsonp(config, callback) {

let {url, params, name, prefix = '_jsonp_callback_', timeout = 60000} = config;

const target = document.getElementsByTagName('script')[0] || document.head;

let script;

let timer;

let callbackFunctionName;

let paramsString = '';

// 定义空函数

function noop() {

}

// 生成随机key值

function getRandomKey(length = 6) {

let randomKey = '';

for (let i = 0; i < length; i++) {

// 生成0~9和a-z的随机字符串

randomKey += ((Math.random() * 36) | 0).toString(36);

}

return randomKey;

}

function checkRandomKey(key, obj) {

// 检查当前生成的key值是否已经存在于obj中

return obj[key] === undefined

? key

: checkRandomKey(getRandomKey(), obj);

}

// 确定挂在window上的回调函数名称

callbackFunctionName = name || checkRandomKey(getRandomKey(), window);

// 清理不需要的代码

function cleanup() {

if (script.parentNode) script.parentNode.removeChild(script);

window[callbackFunctionName] = noop;

if (timer) clearTimeout(timer);

}

// 取消调用

function cancel() {

if (window[callbackFunctionName]) cleanup();

}

// 设置定时器

if (timeout) {

timer = setTimeout(function () {

cleanup();

if (callback) callback(new Error('Timeout'));

}, timeout);

}

// 将传入的params转化为字符串

if (params) {

for (var key in params) {

paramsString += `${key}=${encodeURIComponent(params[key])}&`;

}

}

// 拼接默认的callback内容

paramsString += `callback=${prefix}${callbackFunctionName}`;

// 将回调函数设置到window上

window[callbackFunctionName] = function (data) {

cleanup();

if (callback) callback(null, data);

};

// 将请求参数拼接至url上

url += (~url.indexOf('?') ? '&' : '?') + paramsString;

url = url.replace('?&', '?');

// 创建一个script标签并插入到页面中

script = document.createElement('script');

script.src = url;

target.parentNode.insertBefore(script, target);

// 返回取消函数

return cancel;

}

至此我们完成了我们自己的jsonp轮子。如果发现有问题,欢迎评论区留言。

5. 参考资料

  • github-jsonp
  • 手动实现JSONP

以上是 【JS】「一起造轮子」从1.9k的jsonp库出发&#x1f3d7;一起实现jsonp 的全部内容, 来源链接: utcz.com/a/108923.html

回到顶部