jsonp封装

本文介绍 jsonp 的一种封装方式

jsonp 是一种跨域 ajax 技术

jsonp 请求步骤

  1. 创建 script 标签,设置 src 属性为请求的路径
var script = document.createElement('script');
script.setAttribute('src',url);
  1. 声明全局变量 funcname, 一般是类似
window['funcname'] = function(data){
    console.log(data);
}

这种形式的函数

  1. 监听 script 的 load 事件,在 load 事件清理变量名污染和 script 标签
script.onload = function(){
    delete window['funcname'];
    script.parentNode.removeChild(script);
}
  1. 服务器端以一定的格式返回数据,一般是这种形式
var data = {}
res.end(
    `funcname({
    data : ${JSON.stringfy(data)}
})`
);

如上述所示,jsonp 在使用过程中需要创建标签,声明全局回调函数等繁琐步骤。本文将 jsonp 的细节封装在内部,对外暴露回调的接口。使用者无需考虑 dom 操作,更多考虑在业务层上。

问题的分析与解决

  1. 全局变量污染:通过引入 uuid 方式解决,为每一次 jsonp 请求的回调函数创建一个全球唯一的变量名
  2. 繁琐的 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 拼接操作难以控制
posted @ 2018-01-16 20:58  Salaku  阅读(237)  评论(0编辑  收藏  举报