JSONP原理(2018-08-16)
2018-08-16 本文已影响1人
CRUD_科科
为什么会有跨域问题存在
因为浏览器的同源策略(协议,端口,域名任何一个不同,同源策略都会禁止跨域),原来是浏览器再giao事情:同源策略限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的重要安全机制。
正确展开跨域的几种方式
同源策略是浏览器做的一件好事,是用来防御来自邪门歪道的攻击,但是不能吧我们自己人也挡在门外,这时候就需要正确的打开方式:
- JSONP
在HTML标签里,一些标签比如script、img这样的获取资源的标签是没有跨域限制的,利用这一点,我们可以这样干。通过前端方法作为参数传递到服务器,服务器注入参数后再返还,实现服务器向客户通信,只支持get方法。 - CORS
CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)跨域资源共享 CORS 详解。看名字就知道这是处理跨域问题的标准做法。CORS有两种请求,简单请求和非简单请求。这里不再赘述,请参考阮一峰老师的文章。 - 代理
如果我们请求的时候还是用前端的域名,然后有个东西帮我们把这个请求转发到真正的后端域名上,不就避免跨域了吗?这时候,Nginx出场了。
Nginx配置:
server{
# 监听9099端口
listen 9099;
# 域名是localhost
server_name localhost;
#凡是localhost:9099/api这个样子的,都转发到真正的服务端地址http://localhost:9871
location ^~ /api {
proxy_pass http://localhost:9871;
}
}
跨域Dom查询
参考:https://mp.weixin.qq.com/s/Ldh6rkcimZ1ppHPHK3KeUQ
JSONP的工作流程
- 请求前:创建一个script标签,并给src赋值 url+callback的方法名,并在window上注册这个方法
- 发送请求: 将script添加到页面中
- 数据响应:服务器将返回的数据作为参数和函数名拼接在一起 jsonpCbk({data:”data”})。当浏览器接收到响应数据,由于发起请求的是script,所以相当于直接调用jsonpCbk方法,并且给回调传入了一个参数。
封装jsonp
/**
* JSONP handler
*
* Options:
* - param {String} qs parameter (`callback`)
* - prefix {String} qs parameter (`__jp`)
* - name {String} qs parameter (`prefix` + incr)
* - timeout {Number} how long after a timeout error is emitted (`60000`)
*
* @param {String} url
* @param {Object|Function} optional options / callback
* @param {Function} optional callback
*/
// url要请求的地址以及拼接的参数;opts{param: 约定的函数参数,timeout:超时时间,name:指定的函数名,prefix:指定的函数名前缀};fn回调函数
var count = 0
function noop(){}
function jsonp(url, opts, fn) {
// opts 如果是一个函数 赋值给fn 并重置opts为空对象
if('function' == typeof opts){
fn = opts
opts = {}
}
// 如果没有opts 给opts赋值空对象
if(!opts) opts = {}
var prefix = opts.prefix || '__jp'
// id为opts的name或者prefix+计数(prefix为opts.prefix或__jp)
var id = opts.name || (prefix + (count++))
var param = opts.param || 'callback'
// 有opts.timeout 就取 没有就是60000ms
var timeout = null != opts.timeout ? opts.timeout : 60000
var enc = encodeURIComponent
// 第一个script或者head标签
var target = document.getElementsByTagName('script')[0] || document.head
var script
var timer
// 如果有timeout就在timeout之后执行clean并抛出Error
if (timeout) {
timer = setTimeout(function(){
cleanup()
if(fn) fn(new Error('Timeout'))
}, timeout)
}
function cleanup() {
// 移除创建的script 回调函数置空 清除定时器
if(script.parentNode) script.parentNode.removeChild(script)
window[id] = noop
if(timer) clearTimeout(timer)
}
function cancel() {
if(window[id]) {
cleanup()
}
}
// 全局挂载id
window[id] = function(data) {
cleanup()
// 请求返回后执行回调fn
if(fn) fn(data)
}
url += (~url.indexOf('?') ? '&' : '?') + param + '=' + enc(id);
url = url.replace('?&', '?');
// 创建script标签src属性赋值url
script = document.createElement('script')
script.src = url;
// 插入到head或script的前面
target.parentNode.insertBefore(script, target);
return cancel;
}
此方法在调用时需要自己拼接将参数拼接在url后面 opts如果跟后台有约定callback参数名就传{param: 约定参数名} ,没有默认callback