jsonp封装
本文介绍 jsonp 的一种封装方式
jsonp 是一种跨域 ajax 技术
jsonp 请求步骤
- 创建 script 标签,设置 src 属性为请求的路径
var script = document.createElement('script');
script.setAttribute('src',url);
- 声明全局变量 funcname, 一般是类似
window['funcname'] = function(data){
console.log(data);
}
这种形式的函数
- 监听 script 的 load 事件,在 load 事件清理变量名污染和 script 标签
script.onload = function(){
delete window['funcname'];
script.parentNode.removeChild(script);
}
- 服务器端以一定的格式返回数据,一般是这种形式
var data = {}
res.end(
`funcname({
data : ${JSON.stringfy(data)}
})`
);
如上述所示,jsonp 在使用过程中需要创建标签,声明全局回调函数等繁琐步骤。本文将 jsonp 的细节封装在内部,对外暴露回调的接口。使用者无需考虑 dom 操作,更多考虑在业务层上。
问题的分析与解决
- 全局变量污染:通过引入 uuid 方式解决,为每一次 jsonp 请求的回调函数创建一个全球唯一的变量名
- 繁琐的 dom 操作:将细节封装在内部,在 jsonp 回调函数中进行数据传递和清理操作
uuid 生成器不详细展开
var uuid = function() {
var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split(''), uuid = new Array(36), rnd=0, r;
for (var i = 0; i < 36; i++) {
if (i==8 || i==13 || i==18 || i==23) {
uuid[i] = '-';
} else if (i==14) {
uuid[i] = '4';
} else {
if (rnd <= 0x02) rnd = 0x2000000 + (Math.random()*0x1000000)|0;
r = rnd & 0xf;
rnd = rnd >> 4;
uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r];
}
}
return uuid.join('');
};
dom 操作和 事件监听
var script = document.createElement('script')
var callbackFunctionKey = (opts && opts.callback) || 'callback'
// 避免污染全局变量
var callbackFunctionName = 'jsonp-' + uuid();
script.setAttribute('src', url + '?' + callbackFunctionKey + '=' + callbackFunctionName)
window[callbackFunctionName] = function(data){
callback(data);
// clean
delete window[callbackFunctionName];
script.parentNode.removeChild(script);
}
jsonp 简单封装
function Jsonp(url,callback,opts){
var uuid = function() {
var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split(''), uuid = new Array(36), rnd=0, r;
for (var i = 0; i < 36; i++) {
if (i==8 || i==13 || i==18 || i==23) {
uuid[i] = '-';
} else if (i==14) {
uuid[i] = '4';
} else {
if (rnd <= 0x02) rnd = 0x2000000 + (Math.random()*0x1000000)|0;
r = rnd & 0xf;
rnd = rnd >> 4;
uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r];
}
}
return uuid.join('');
};
var script = document.createElement('script')
var callbackFunctionKey = (opts && opts.callback) || 'callback'
// 避免污染全局变量
var callbackFunctionName = 'jsonp-' + uuid();
script.setAttribute('src', url + '?' + callbackFunctionKey + '=' + callbackFunctionName)
window[callbackFunctionName] = function(data){
callback(data);
// clean
delete window[callbackFunctionName];
script.parentNode.removeChild(script);
}
document.body.appendChild(script)
}
后端返回数据格式
const express = require('express');
const app = express();
app.get('/', function(req, res){
var funcname = req.query['callback'];
res.end( `
window['${funcname}']({
data : 'data'
})
`);
console.log('get');
})
app.listen(3000);
更进一步
- opts 可以支持其他参数
- url 拼接操作难以控制