首先了解什么是同源策略:
限制一个源加载的文档或脚本与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的关键的安全机制。(来自MDN的解释)
源包括三个部分:协议、域名、端口(HTTP协议的默认端口是80)。如果其中有任何一个部分不同,则源不同。即为跨域。
限制:这个源的文档没有权利去操作另一个源的文档。这个限制体现在:
Cookie、LlocalStorage和IndexDB无法获取;
无法获取和操作DOM;
不能发送Ajax请求。(Ajax只适合同源的通信)。
前后端的通信方式:
Ajax:不支持跨域;
WebSocket:不受同源策略的限制,支持跨域;
CORS:不受同源策略的限制,支持跨域;一种新的通信协议标准。可以理解成同时支持同源和跨域的Ajax。
如何创建Ajax:
在回答 Ajax 的问题时,要回答以下几个方面:
-
1、XMLHttpRequest 的工作原理
-
2、兼容性处理
XMLHttpRequest只有在高级浏览器中才支持。在回答问题时,这个兼容性问题不要忽略。
-
3、事件的出发条件
-
4、事件的触发顺序
XMLHttpRequest有很多触发事件,每个事件是怎么触发的。
发送Ajax请求的五个步骤(XMLHttpRequest的工作原理)
1.创建XMLHttpRequest对象;
2.使用open方法设置请求的参数。open(method,url,是否异步)
3.发送请求;
4.注册事件。使用onreadystatechange事件,状态改变时就会调用。如果要在数据完整请求返回才调用需要加一些状态判断;
5.获取返回的数据,更新UI
发送get请求和post请求:
get请求举例:Document Ajax 发送 get 请求
post请求举例:
Document Ajax 发送 get 请求
onreadystatechange事件:
注册onreadystatechange事件后,每当readyState属性改变时,就会调用onreadystatechange函数。
readyState:(存有XMLHttpRequest的状态。从0到4变化)
0:请求未初始化;
1:服务器连接已建立;
2:请求已接收;
3:请求处理中;
4:请求已完成,且响应已就绪
事件的触发条件
事件的触发顺序
原生Ajax请求
var util = {}; //获取 ajax 请求之后的json util.json = function (options) { var opt = { url: '', type: 'get', data: {}, success: function () { }, error: function () { }, }; util.extend(opt, options); if (opt.url) { //IE兼容性处理:浏览器特征检查。检查该浏览器是否存在XMLHttpRequest这个api,没有的话,就用IE的api 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); // 如果想要使用post提交数据,必须添加此行 xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); xhr.send(dataArr.join('&')); } xhr.onload = function () { if (xhr.status === 200 || xhr.status === 304) { //304表示:用缓存即可。206表示获取媒体资源的前面一部分 var res; if (opt.success && opt.success instanceof Function) { res = xhr.responseText; if (typeof res === 'string') { res = JSON.parse(res); //将字符串转成json opt.success.call(xhr, res); } } } else { if (opt.error && opt.error instanceof Function) { opt.error.call(xhr, res); } } }; }
跨域通信的几种方式:
1.JSONP
2.WebSocket
3.CORS
4.Hash
5.postMessage
1.JSONP
原理:通过<script>标签的异步加载来实现。例如通过<script>标签的src,里面放url,加载一些插件,就用到JSONP。
实现:<script src="http://www.smyhvae.com/?data=name&callback=myjsonp"></script>
上面的src中,data=name
是get请求的参数,myjsonp
是和后台约定好的函数名。
服务器端这样写:
myjsonp({ data: {} })
于是,本地要求创建一个myjsonp 的全局函数,才能将返回的数据执行出来。
实际开发中前端的JSONP的实现:
2.WebSocket
代码实例如下:
var ws = new WebSocket('wss://echo.websocket.org'); //创建WebSocket的对象。参数可以是 ws 或 wss,后者表示加密。 //把请求发出去 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.'); };
3.CORS
fetch是一个比较新的API,用来实现CORS通信。用法如下:
// url(必选),options(可选) fetch('/some/url/', { method: 'get', }).then(function (response) { //类似于 ES6中的promise }).catch(function (err) { // 出错了,等价于 then 的第二个参数,但这样更好用更直观 });
另外,如果面试官问:“CORS为什么支持跨域的通信?”
答案:跨域时,浏览器会拦截Ajax请求,并在http头中加Origin。
4.Hash
url的#
后面的内容就叫Hash。Hash的改变,页面不会刷新。这就是用 Hash 做跨域通信的基本原理。
补充:url的?
后面的内容叫Search。Search的改变,会导致页面刷新,因此不能做跨域通信。
场景:我的页面 A 通过iframe或frame嵌入了跨域的页面 B。
现在,我这个A页面想给B页面发消息,怎么操作呢?
(1)首先,在我的A页面中:
//伪代码 var B = document.getElementsByTagName('iframe'); B.src = B.src + '#' + 'jsonString'; //我们可以把JS 对象,通过 JSON.stringify()方法转成 json字符串,发给 B
(2)然后,在B页面中:
// B中的伪代码 window.onhashchange = function () { //通过onhashchange方法监听,url中的 hash 是否发生变化 var data = window.location.hash; };
5.postMessage()方法
场景:窗口 A (http:A.com
)向跨域的窗口 B (http:B.com
)发送信息。步骤如下。
(1)在A窗口中操作如下:向B窗口发送数据:
// 窗口A(http:A.com)向跨域的窗口B(http:B.com)发送信息 Bwindow.postMessage('data', 'http://B.com'); //这里强调的是B窗口里的window对象
(2)在B窗口中操作如下:
// 在窗口B中监听 message 事件 Awindow.addEventListener('message', function (event) { //这里强调的是A窗口里的window对象 console.log(event.origin); //获取 :url。这里指:http://A.com console.log(event.source); //获取:A window对象 console.log(event.data); //获取传过来的数据 }, false);
参考:
https://www.cnblogs.com/smyhvae/p/8523576.html