“我报名参加金石计划1期挑战——瓜分10万奖池,这是我的第2篇文章,点击查看活动详情”
为什么会产生跨域?
这是浏览器的同源策略,当一个前端项目向后端项目发请求,或者说这两个项目要进行通信的话,那么这两个项目的这三个地址都要相同即:协议号 和 域名 和 端口号 都相同,这样才符合浏览器的同源策略。
协议号 和 域名 和 端口号
https://( 协议号) www.juejin.cn (域名) :8080 (端口号) ,如果这三个有一个不相同就不符合跨域策略。
跨域通常发生在什么时候?
跨域通常发生在后端响应回来的数据,在浏览器接收到时被跨域机制拦截下来
解决跨域的方案---JSONP
这个方法就是通过script的src属性加载资源时不受同源资源策略的影响这一特性,就是我们自己在前端定义一个jsonp方法,在方法里面创建一个script标签,然后将script标签添加到body体里面,这样浏览器就能读取到script标签的src资源,我们就是借助这个属性上面的src值向这个值发src请求,这样后端就会接收到这个请求(在src里面我们传入几个参数和一个函数名的字符串形式);然后等后端接受到前端传过来的参数,然后我们在后端将这几个参数拼接起来变成一个函数调用的样子然后返回给前端,前端就拿到了一个函数调用的字符串形式,现在前端script的src属性就是后端传过来的一个函数调用的字符串形式。注意:浏览器加载script标签的src属性时,就算拿到的是一个字符串,但是浏览器也会将这个字符串执行,所以就相当于将后端传过来的函数调用的字符串形式执行掉,而src请求回来的资源会被window执行。
前端代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.1/jquery.js"></script>
<title>Document</title>
</head>
<body>
<button id="btn">获取数据</button>
<script>
// 自己声明一个jsonp函数
const jsonp=(url,params,cb)=>{ // 地址 对象 函数 可以看下面调用时的实参来理解
return new Promise((resolve,reject)=>{
// 在js里面创建一个script标签,我们就是用script标签里面的src属性
const script =document.createElement('script')
params={...params,cb:cb}
const arr = Object.keys(params).map(key => `${key}=${params[key]}`) // 得到一个新数组,里面存着这样子的东西['name="bruce",...],就是将后面两个参数融合到一起,方便下面拼接
script.src=`${url}?${arr.join('&')}` // 这个地址要被添加到页面的html里面浏览器才能加载
document.body.appendChild(script) // 将这个script标签添加到body体里面
// 在window上声明的一个函数,需要后端返回的东西触发它
window[cb]=(data)=>{ // data 就是script标签src属性加载的资源
resolve(data)
}
})
}
let btn =document.getElementById('btn')
btn.addEventListener('click',()=>{
jsonp('http://localhost:3000',{name:'bruce',age:18,},'callback')
.then(res=>{
console.log(res);
})
})
</script>
</body>
</html>
后端
const Koa= require('koa')
const app=new Koa()
const main=(ctx,next)=>{
console.log(ctx.query);
const{name,age,cb}=ctx.query;
const userInfo=`${name}今年${age}岁`
const str =`${cb}(${JSON.stringify(userInfo)})` // 后端将前端传过来的值拼接成一个字符串 函数调用的样子'callback()'
// ctx.body='hello world'
ctx.body=str //将字符串输出给前端
}
app.use(main)
app.listen(3000,()=>{
console.log('3000端口已启动');
})