本文已参与掘金创作者训练营第三期「高产更文」赛道,详情查看:掘力计划|创作者训练营第三期正在进行,「写」出个人影响力。
什么是跨域?
我们的浏览器处于安全方面的考虑,只允许 Ajax 访问本域下的接口。如果没有授权,是不能够跨域的。
上面这句话,也叫做:同源策略。
那么,什么算是本域呢?
- 同协议:比如都是 http 或者都是 https
- 同域名:比如 github.com/pany-ang 和 github.com/v3-projects…
- 同端口:比如都是 80 端口(如果域名中没有写明端口号,那么 http 默认是 80 端口,https默认 443)
同时做到以上三点就是本域。
举个例子:
github.com/pany-ang 和 github.com/v3-projects…
上面这两个域名就不存在跨域,它们协议都是 https,域名都是 github.com,端口都是 443。
也就是说,当我们的页面发送 Ajax 请求时,如果访问的服务器接口不是本域的接口,那么就跨域了(即当前页面所在的域名和接口所在的域名不是"本域")。
怎么解决跨域问题?
既然浏览器不允许我们跨域,但我们又想要实现跨域调用接口,这时候我们就要想办法允许我们的应用跨域。
JSONP
这个方法很有年代感了,现在用到的次数并不多了。
在讲这个实现之前,我们要明白一个东西:
<script src="这里是JS文件的路径"></script>
上面这段代码的意思就是普通的引入一个外部 JS,想必大家都明白。它最终的效果相当于:
<script>这里是引入的JS文件里的源码</script>
但是这段代码还可以这样写:
<script src="这里是后端提供的接口的路径"></script>
它最终的效果相当于:
<script>这里是后端接口返回的数据</script>
没有错,当我们通过 Ajax 访问接口会涉及跨域时,通过上面的代码就不会被判定为跨域。
通过这个办法,我们不用担心跨域就可以将接口返回的数据拿到手。
但是问题来了,
我们拿到的数据一般是 JSON 格式的,而且数据就单单只是数据,它是 “死” 的。我们通常是通过 AJAX 拿到数据后再用一个回调函数去处理它,但是现在我们用这个 “巧妙” 的办法拿到数据后好像就没有办法去处理它了。
这时候我们就要这样做:后端返回数据时加一个函数名: "数据" 变为 "函数名(数据)",将原本的数据,放到参数的位置上去。
"数据" 变为 "函数名(数据)" 最终导致的效果就是:
<script>这里是后端接口返回的数据</script>
变为:
<script>函数名(数据)</script>
如果返回的数据前有一个函数名,并且我们的前端代码里也有一个事先写好的函数与它同名,那么就相当于调用了我们前端代码里的这个函数。我们就可以通过这个函数来处理返回的数据。
CORS
这里我只简单介绍一下 CORS 方式实现跨域(其实它本来也很简单...)
原理:发送 Ajax 请求的时候,如果跨域了,浏览器就会自动帮我们给该请求加一个请求头 Origin ,由后台判断该不该返回数据,如果后台判断为 “应该返回数据”,就在返回结果中加入一个响应头 Access-Control-Allow-Origin;这时浏览器就判断响应头中有没有 Origin 的值,如果有,我们就可以成功跨域拿到数据,如果没有,就跨域失败。
用这个方法,前端基本上不用做什么改动,后端需要添加一条类似的语句:
res.header("Access-Control-Allow-Origin", "*"); // * 表示允许所有域名访问
反向代理
原理:既然跨域问题是浏览器的同源策略引起的,那么我们不直接经过浏览器去调后端接口,而是启用一个中间代理服务,由它去请求相应的接口就行了。
以 Vue2 为例,开发环境配置如下:
proxy: {
'/api/': {
target: 'http://xxxxxx/api/',
ws: true,
pathRewrite: {
'^/api/': ''
},
changeOrigin: true,
secure: false
}
}
与之对应的生产环境,则可以使用 nginx
来做反向代理。
其他
当然,还有其他的一些解决跨域的场景,比如 降域、postMessage,但这些都是常用来解决纯前端的跨域问题(比如 A 访问 B 页面的数据),这篇文章就不做过多解释了。