大家好,今天我们一起看下前端面试的经典问题:什么是跨域?首先拿到问题后,我们需要先思考下问这个问题的人即面试官,他希望获得到什么答案。下面,我们可以模拟下面试官可能是怎么想的:
- 第一种:遇到跨域问题,我觉得这里有一两个点还挺有意思,想知道你知不知道,能不能答出来
- 第二种:跨域就是基础,一般都能答出来,看看你的知识面,想往内知识点再深入挖点,看有没有惊喜
- 第三种:网上都是这些问题,我也问问看
一般情况就这几种情况了,那针对这几种情况我们该怎么去回答才能让面试官满意呢?我们一个个来分析剖析下。
第一种情况其实最难搞,因为面试官他心里已经有一个比较明确的知识点,且是个人解决的经验,而这个经验存在一个问题:即并不一定是所有人都遇到过,但是要是不了解,面试官可能又会觉得你不行,这个人不行,怎么这个问题都不知道。那如何解决或者说是避免这个问题呢?
其实,这种情况也不用太担心,我们只需要在问题的基础上再加一个小小的点就行了:个人见解和项目问题结合,哪怕你对于他预设的答案不知道,一般也是能让面试官满意的,因为这能说明我们对这个问题做了思考,做项目的时候做了思考。
第二种这个就需要我们对于问题中的单个知识点比较熟悉,就比如跨域这个问题,我们可能涉及到几个方面的知识点都需要我们有所准备,有所了解:
- JSONP怎么使用?实现原理
- CORS怎么配置?除了CORS的头部配置外,你还知道哪些常用的请求头配置,具体又是为了解决哪些问题?
- 代理的原理是什么?
- iframe怎么使用?有哪些属性?和其他元素比iframe有什么不同?
- websocket怎么实现通信的?http与socket通信的区别是什么?什么情况下会使用socket通信?
- …等等
这种情况就比较常规了,我们只要根据最经典的 “3W”原则去回答就行了,即:
- 什么是跨域?
- 跨域出现的场景
- 怎么解决跨域问题?
- 基本按章回答列出来就行。
通过上述的分析,我们来总结下最有回答方案。首先第一种情况和第三种情况较为相似,我们将问题拼接下,第二种情况比较看重的是对于知识面的了解,可能会延伸到偏原理方面的知识,对于自己不熟悉的知识点要及时认怂。这样,我们可以这样对问题进行分解。
- 什么是跨域?
- 跨域出现的场景
- 怎么解决跨域?
- 结合项目的见解
- 跨域延伸问题了解
如上图所示,浏览器出于安全考虑,只允许访问请求在相同域名相同端口相同协议下的脚本和接口。协议、域名、端口有一个不满足的话 就会造成跨域。这种设置又被称呼为同源策略。
- 网络请求
- cookie、localStorage、sessionStorage访问
借助script标签,有src属性,所以可以发出网络请求,浏览器不限制script标签引用其他域的资源(浏览器会将返回的内容,作为js代码执行)。
<script src="http://jsonp.js?callback=cb"></script> // 或 let script = document.createElement('script'); script.src = "http://jsonp.js?callback=cb"; body.append(script)
特点
- Jsonp只支持get请求
- 兼容性比较好,可用于解决主流浏览器的跨域数据访问的问题
- 不受到同源策略的限制,在请求完毕后可以通过调用 callback 的方式回传结果
- 具有局限性,不安全,可能会受到XSS攻击
- 只支持跨域 HTTP 请求这种情况,不能解决不同域的两个页面之间如何进行 Javascript 调用的问题
CORS 机制支持浏览器和服务器之间的安全跨域请求和数据传输。CORS允许我们通过服务器设置响应头进行跨域请求。
服务器通过响应头控制跨域类型,
Access-Control-Allow-Origin:http://foo.example // 控制可跨域的origin域名,对应请求头:Origin Access-Control-Allow-Methods:POST,GET,OPTIONS // 控制可跨域的方法 Access-Control-Allow-Headers:Token,X-OPTIONS // 控制可请求的自定义请求头名,如常见的前端token验证
3.反向代理
上图是反向代理的一个流程图,我们可以通过配置服务器的反向代理,通过代理服务请求目标服务并将返回接口返回给客户端。
// webpack.config.jsmodule.exports = { devServer: {// 代理配置 proxy: { // 这里的api 表示如果我们的请求地址有/api的时候,就出触发代理机制 // http://localhost:9588/api/abc => 代理给另一个服务器 // 本地的前端 =》 本地的后端 =》 代理我们向另一个服务器发请求 (行得通) // 本地的前端 =》 另外一个服务器发请求 (跨域 行不通) '/api': { target: 'www.baidu.com', // 打比方这是我们要代理的地址changeOrigin: true, // 是否跨域 需要设置此值为true 才可以让本地服务代理我们发出请求 // 路径重写 pathRewrite: { // 假设我们想把 http://localhost:9588/api/abc 变成www.baidu.com/abc 就需要这么做 '^/api': '' } }, } }}
特点:
- 隐藏服务器IP:使用反向代理,可以对客户端隐藏服务器的IP地址 。
- 负载均衡:反向代理服务器可以做负载均衡,根据所有真实服务器的负载情况,将客户端请求分发到不同的真实服务器上。
- 提高访问速度:反向代理服务器可以对于静态内容及短时间内有大量访问请求的动态内容提供缓存服务,提高访问速度。
4.websocket
使用websocket通信,就省去了通过http协议的请求头方式进行约定式的跨域设定,websocket通信是天然跨域的方式。使用方式如下:
// 前端部分 <script> let socket = new WebSocket("ws://localhost:8080"); socket.onopen = function() {socket.send("秋风的笔记");}; socket.onmessage = function(e) {console.log(e.data);}; </script> // nodejs 后端部分 const WebSocket = require("ws"); const server = new WebSocket.Server({ port: 8080 }); server.on("connection", function(socket) { socket.on("message", function(data) { socket.send(data); }); });
前端我们可以通过iframe嵌套其他页面,然后通过postMessage实现多个iframe之前的跨域通信,从而解决跨域问题。使用方式如下:
- a.html
<body> <button onclick="postTestMessage()">postMessage</button> <iframe src="./b.html" frameborder="0" id="iframe"></iframe> </body> <script> let receiveMessage = function(event) { console.log('a:', event); let datas = JSON.parse(event.data); if (datas.type === "advert") { let postIframeData = { type:'adGivePrize', givePrize:true }; //iframe发送信息~~~~ window.frames[0].postMessage(JSON.stringify(postIframeData), ''); // window.frames[0].postMessage('a页面', '');}} window.addEventListener("message", receiveMessage, false); function postTestMessage() { let defaultAdData = { type:'advert', gameData:{ adId: '123' } }; window.postMessage(JSON.stringify(defaultAdData), '*'); } </script>
- b.html
<body> <h1>我是b页面</h1></body> <script> var receiveMessage = function(event) { console.log('b: ', JSON.parse(event.data)); } window.addEventListener("message", receiveMessage, false); </script>
如上实现a中嵌套的b.html的跨域访问。
最后我们可以结合我们项目中遇到的问题谈谈跨域。比如,我在使用jspdf过程中处理img图片产生跨域问题,然后可以谈下:
- img 标签为何会产生跨域?img标签应该不存在跨域问题,参考Jsonp
- 如何解决的?
就上面这个问题是因为:通过 dom 节点的 标签来直接访问是没有问题,因为浏览器本身不会有跨域问题。而图片再次被复用到 canvas 上去时,就报跨域错误。我们可以通过增加crossOrigin属性,使得图片使用时不会被污染而存在使用限制(跨域)。还可以通过配置cors,使用xhr方式请求图片换成base64本地资源解决跨域问题。
注意:这个枚举属性表明是否必须使用 CORS 完成相关图像的抓取。启用 CORS 的图像可以在元素中重复使用,而不会被污染。允许的值有:
- anonymous:执行一个跨域请求。但是没有发送证书。
- use-credentials:一个有证书的跨域请求。