WebWeb跨域浏览器跨域通信
余生杂货铺1 前言
前端通信类的问题,主要包括以下内容:
- 什么是同源策略及限制
同源策略是一个概念,就一句话。有什么限制,就三句话。能说出来即可。
- 前后端如何通信
如果你不准备,估计也就只能说出ajax
。这个可以考察出知识面。
- 如何创建Ajax
Ajax
在前后端通信中经常用到。做业务时,可以借助第三方的库,比如vue
框架里的库、jQuery
也有封装好的方法。但如果让你用原生的js
去实现,该怎么做?
这就是考察你的动手能力,以及框架原理的掌握。如果能写出来,可以体现出你的基本功。是加分项。
- 跨域通信的几种方式
这部分非常重要。无非就是问你:什么是跨域、跨域有什么限制、跨域有几种方式。
下面分别讲解。
2 同源策略的概念和具体限制
同源策略:限制从一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的关键的安全机制。(来自MDN官方的解释)
具体解释:
源
包括三个部分:协议、域名、端口(http
协议的默认端口是80
)。如果有任何一个部分不同,则源
不同,那就是跨域了。
限制
:这个源的文档没有权利去操作另一个源的文档。这个限制体现在:(要记住)
Cookie
、LocalStorage
和IndexDB
无法获取。
- 无法获取和操作
DOM
。
- 不能发送
Ajax
请求。我们要注意,Ajax
只适合同源的通信。
3 前后端如何通信
主要有以下几种方式:
Ajax
:不支持跨域。
WebSocket
:不受同源策略的限制,支持跨域
CORS
:不受同源策略的限制,支持跨域。一种新的通信协议标准。可以理解成是:同时支持同源和跨域的Ajax。
4 如何创建Ajax
在回答 Ajax
的问题时,要回答以下几个方面:
XMLHttpRequest
的工作原理
- 兼容性处理
XMLHttpRequest
只有在高级浏览器中才支持。在回答问题时,这个兼容性问题不要忽略。
- 事件的触发条件
- 事件的触发顺序
XMLHttpRequest
有很多触发事件,每个事件是怎么触发的。
4.1 发送 Ajax 请求的五个步骤(XMLHttpRequest的工作原理)
- 创建
XMLHttpRequest
对象。
- 使用
open
方法设置请求的参数。`open(method, url, 是否异步)``。
- 发送请求。
- 注册事件。 注册
onreadystatechange
事件,状态改变时就会调用。
如果要在数据完整请求回来的时候才调用,我们需要手动写一些判断的逻辑。
- 获取返回的数据,更新UI。
4.2 发送 get 请求和 post 请求
get
请求举例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <h1>Ajax 发送 get 请求</h1> <input type="button" value="发送get_ajax请求" id='btnAjax'>
<script type="text/javascript"> document.querySelector('#btnAjax').onclick = function () {
var ajaxObj = new XMLHttpRequest();
ajaxObj.open('get', '02-ajax.php');
ajaxObj.send();
ajaxObj.onreadystatechange = function () { if (ajaxObj.readyState == 4 && ajaxObj.status == 200) { console.log('数据返回成功');
console.log(ajaxObj.responseText);
document.querySelector('h1').innerHTML = ajaxObj.responseText; } } } </script> </body> </html>
|
post
请求举例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <h1>Ajax 发送 get 请求</h1> <input type="button" value="发送put_ajax请求" id='btnAjax'> <script type="text/javascript">
var xhr = new XMLHttpRequest();
xhr.open('post', '02.post.php');
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xhr.send('name=fox&age=18');
xhr.onreadystatechange = function () { if (xhr.readyState == 4 && xhr.status == 200) { alert(xhr.responseText); } }; </script> </body> </html>
|
4.3 onreadystatechange 事件
注册 onreadystatechange
事件后,每当 readyState
属性改变时,就会调用 onreadystatechange
函数。
readyState
:(存有 XMLHttpRequest
的状态。从 0
到 4
发生变化)
0
: 请求未初始化
1
: 服务器连接已建立
2
: 请求已接收
3
: 请求处理中
4
: 请求已完成,且响应已就绪
4.4 事件的触发条件
4.5 事件的触发顺序
4.6 实际开发中用的 原生Ajax请求
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
| var util = {};
util.json = function (options) {
var opt = { url: '', type: 'get', data: {}, success: function () { }, error: function () { },
}; util.extend(opt, options); if (opt.url) { var xhr = XMLHttpRequest ? new XMLHttpRequest() : new window.ActiveXObject('Microsoft.XMLHTTP');
var data = opt.data, url = opt.url, type = opt.type.toUpperCase(); dataArr = []; }
for (var key in data) { dataArr.push(key + '=' + data[key]); }
if (type === 'GET') { url = url + '?' + dataArr.join('&'); xhr.open(type, url.replace(/\?$/g, ''), true); xhr.send(); }
if (type === 'POST') { xhr.open(type, url, true); xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); xhr.send(dataArr.join('&')); }
xhr.onload = function () { if (xhr.status === 200 || xhr.status === 304) { var res; if (opt.success && opt.success instanceof Function) { res = xhr.responseText; if (typeof res === 'string') { res = JSON.parse(res); opt.success.call(xhr, res); } } } else { if (opt.error && opt.error instanceof Function) { opt.error.call(xhr, res); } } }; }
|
5 跨域通信的几种方式
方式如下:
JSONP
WebSocket
CORS
Hash
postMessage
上面这五种方式,在面试时,都要说出来。
5.1 JSONP
面试会问:JSONP
的原理是什么?怎么实现的?
- 在
CORS
和postMessage
以前,我们一直都是通过JSONP
来做跨域通信的。
JSONP的原理:通过<script>
标签的异步加载来实现的。比如说,实际开发中,我们发现,head
标签里,可以通过<script>
标签的src
,里面放url
,加载很多在线的插件。这就是用到了JSONP
。
JSONP的实现:
比如说,客户端这样写:
1
| <script src="http://www.smyhvae.com/?data=name&callback=myjsonp"></script>
|
上面的src
中,data=name
是get请求的参数,myjsonp
是和后台约定好的函数名。
服务器端这样写:
于是,本地要求创建一个myjsonp
的全局函数,才能将返回的数据执行出来。
实际开发中,前端的JSONP是这样实现的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| <script>
var util = {};
util.createScript = function (url, charset) { var script = document.createElement('script'); script.setAttribute('type', 'text/javascript'); charset && script.setAttribute('charset', charset); script.setAttribute('src', url); script.async = true; return script; };
util.jsonp = function (url, onsuccess, onerror, charset) { var callbackName = util.getName('tt_player'); window[callbackName] = function () { if (onsuccess && util.isFunction(onsuccess)) { onsuccess(arguments[0]); } }; var script = util.createScript(url + '&callback=' + callbackName, charset); script.onload = script.onreadystatechange = function () { if (!script.readyState || /loaded|complete/.test(script.readyState)) { script.onload = script.onreadystatechange = null; if (script.parentNode) { script.parentNode.removeChild(script); } window[callbackName] = null; } }; script.onerror = function () { if (onerror && util.isFunction(onerror)) { onerror(); } }; document.getElementsByTagName('head')[0].appendChild(script); };
</script>
|
5.2 WebSocket
WebSocket
的用法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
var ws = new WebSocket('wss://echo.websocket.org');
ws.onopen = function (evt) { console.log('Connection open ...'); ws.send('Hello WebSockets!'); };
ws.onmessage = function (evt) { console.log('Received Message: ', evt.data); ws.close(); };
ws.onclose = function (evt) { console.log('Connection closed.'); };
|
面试一般不会让你写这个代码,一般是考察你是否了解 WebSocket
概念,知道有这么回事即可。
5.3 CORS
CORS
可以理解成是既可以同步、也可以异步的Ajax。
- fetch
是一个比较新的
API,用来实现
CORS`通信。用法如下:
1 2 3 4 5 6 7 8
| fetch('/some/url/', { method: 'get', }).then(function (response) {
}).catch(function (err) { });
|
另外,如果面试官问:“CORS为什么支持跨域的通信?”
答案:跨域时,浏览器会拦截Ajax
请求,并在http
头中加Origin
。
5.4 Hash
url
的#
后面的内容就叫Hash
。Hash的改变,页面不会刷新。这就是用 Hash
做跨域通信的基本原理。
补充:url
的?
后面的内容叫Search
。Search
的改变,会导致页面刷新,因此不能做跨域通信。
使用举例:
场景:我的页面 A
通过iframe
或frame
嵌入了跨域的页面 B
。
现在,我这个A
页面想给B
页面发消息,怎么操作呢?
- 首先,在我的
A
页面中:
1 2 3
| var B = document.getElementsByTagName('iframe'); B.src = B.src + '#' + 'jsonString';
|
- 然后,在
B
页面中:
1 2 3 4
| window.onhashchange = function () { var data = window.location.hash; };
|
5.5 postMessage()方法
H5
中新增的`postMessage()``方法,可以用来做跨域通信。既然是H5中新增的,那就一定要提到。
场景:窗口 A (http:A.com
)向跨域的窗口 B (http:B.com
)发送信息。步骤如下
- 在
A
窗口中操作如下:向B
窗口发送数据:
1 2
| Bwindow.postMessage('data', 'http://B.com');
|
- 在
B
窗口中操作如下:
1 2 3 4 5 6
| Awindow.addEventListener('message', function (event) { console.log(event.origin); console.log(event.source); console.log(event.data); }, false);
|