在浏览器中有一个同源策略, 也是浏览器的安全策略, 保障资源间的安全访问,我们访问地址的URL: 协议://域名:端口/资源路径?查询字符串#hash,也就是只有当协议,域名,端口完全一致的时候 才能访问,只要有一个不同,就会出现跨域;
el.onClick = function () {
var val = '参数'
var st = document.createElement("script"); // 创建script标签
st.src = 'https://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su?wd='+ val + '&cb=call' // call就是回调函数名
document.body.appendChild(st); // 将script标签添加到body中,就会执行全局的回调函数,所以直接在下面删除就可以了
// 删除script
st.parentNode.removeChild(st)
}
function call(data) {
// 创建全局回调函数
console.log(data) // data就是返回的数据
}
在vue中使用JSONP跨域访问数据,并且使用promise封装:
import jsonp from 'jsonp'
const parseParam = (data) => {
// 这个函数要把一个对象换成这样的形式 'key=1&key1=2'的这种形式
let params = []
Object.keys(data).forEach((item) => {
params.push([item, encodeURIComponent(data[item])])
})
return params.map(item => item.join('=')).join('&')
}
export default (url, urldata, options) => {
/**
* 这里的url: 就是服务器地址
* data: 就是请求地址的时候携带的参数
* options: 是jsonp这个包的第二个参数,是一个对象,取值看下面
* */
url += (url.indexOf('?') < 0 ? '?' : '&') + parseParam(urldata)
return new Promise((resolve, reject) => {
/***
* url是服务器地址,也包括参数
* options则是jsonp这个包当中的第二个参数
* param (String) 用于指定回调的查询字符串参数的名称 (默认 'callback')
* timeout (Number) 发出超时错误后多长时间 (默认 : 60000)
* prefix (String) 处理JSONP响应的全局回调函数的前缀 (默认 '__jp')
* name (String) 处理JSONP响应的全局回调函数的名称 (默认: prefix + incremented counter)
* jsonp的第三个参数是一个回调函数,
* 这个回调函数中有两个参数(err, data), 当是err的时候,将取消正在进行的jsonp请求, data这是请求成功时候的数据
*
* */
jsonp(url, options, (err, data) => {
if (err) {
reject(err)
} else {
resolve(data)
}
})
})
}
然后在请求数据的地方引入下面的代码,我的文件目录是在 api/xxx.js
import axios from 'axios' // 可以直接在axios文件下面引入,因为这里可能还有通过axios请求的方法
import jsonp from '@/assets/js/jsonp.js'
export const getBestData = (page = 1, psize = 20) => {
const url = '服务器地址'
const params = {
// 这个是上传服务器的参数
name: '参数'
}
return jsonp(url, params, {
param: 'callback', // 制定回调函数名
}).then(res => {
if (res.code === '200') {
return res
}
throw new Error('没有成功获取到数据!') // 注意在这里如果状态码不等于200, 则手动抛出错误
}).catch(err => {
// 那在catch中可以捕获到所有的错误, 包括手动抛出的,如果出现错误,就报错
if (err) {
console.log(err)
}
}).then(res => {
// 这个then呢就是在上面的then成功, res就是上面return的数据,所以只要走到这个then的时候,那肯定就会有数据的
return new Promise(resolve => {
// 当有数据的时候,就返回一个promise对象这样写的主要原因是因为setTimeout是一个异步函数
resolve(res)
})
// return res // 如果不要loading效果,直接写res也可以
})
}
实现思路: 主要通过ifarme标签加载跨域资源不受浏览器同源策略限制,下面新建两个html文件,a.html 、b.html
a.html代码
<body>
<h1>这是a.html</h1>
<!-- 在a页面中嵌套b页面,就可以通过iframe -->
<iframe src="http://localhost:4000/b.html" frameborder="0" id="frame"></iframe>
<script>
let frame = document.getElementById('frame');
frame.onload = function () {
// 获取frame标签中的window, 在a页面给b页面发送消息
frame.contentWindow.postMessage('在a页面给b页面发送消息', 'http://localhost:4000');
// 获取b页面的消息
window.onmessage = function (e) {
console.log(e)
console.log(e.data)
}
}
</script>
</body>
b.html代码
<body>
<h1>这是b.html</h1>
<script>
// 在b页面接收a页面发送的消息
window.onmessage = function (e) {
// e.data就是a页面发送过来的消息
console.log(e.data)
// 如果b页面也要给a页面返回消息,那就要获取到a页面的window这个对象
e.source.postMessage('b页面返回给页面的消息', e.origin)
}
</script>
</body>
<body>
<iframe src="http://localhost:4000/c.html" frameborder="0" id="frame"></iframe>
<script>
let frame = document.getElementById('frame');
let first = true;
frame.onload = function () {
if(first) {
// 当frame的地址改变的时候,那iframe标签就会从新出发onload事件
frame.src = 'http://localhost:3000/b.html';
first = false
} else {
// 所以当第二次加载的时候,就走else, 主要利用了window.name,当域名发生变化的时候,window.name还是存在的
console.log(frame.contentWindow.name)
}
}
</script>
</body>
c.html代码:
<body>
<script>
window.name = '你好,我是c页面的数据'
</script>
</body>
那b.html就是空的html文件,b就相当于是个跑龙套的,但是必须保证b.html和a.html是同域的
可获取上述跨域的代码