本文最初发表于博客园,并在GitHub上持续更新前端的系列文章。欢迎在GitHub上关注我,一块儿入门和进阶前端。javascript
如下是正文。php
前端通讯类的问题,主要包括如下内容:html
同源策略是一个概念,就一句话。有什么限制,就三句话。能说出来便可。前端
若是你不许备,估计也就只能说出ajax。vue
Ajax在先后端通讯中常常用到。作业务时,能够借助第三方的库,好比vue框架里的库、jQuery也有封装好的方法。但若是让你用原生的js去实现,该怎么作?java
这就是考察你的动手能力,以及框架原理的掌握。若是能写出来,能够体现出你的基本功。git
这部分很是重要。无非就是问你:什么是跨域、跨域有什么限制、跨域有几种方式。github
下面分别讲解。web
同源策略:限制从一个源加载的文档或脚本如何与来自另外一个源的资源进行交互。这是一个用于隔离潜在恶意文件的关键的安全机制。(来自MDN官方的解释)面试
具体解释:
(1)源
包括三个部分:协议、域名、端口(http协议的默认端口是80)。若是有任何一个部分不一样,则源
不一样,那就是跨域了。
(2)限制
:这个源的文档没有权利去操做另外一个源的文档。这个限制体如今:(要记住)
Cookie、LocalStorage和IndexDB没法获取。
没法获取和操做DOM。
不能发送Ajax请求。咱们要注意,Ajax只适合同源的通讯。
主要有如下几种方式:
Ajax:不支持跨域。
WebSocket:不受同源策略的限制,支持跨域。
CORS:不受同源策略的限制,支持跨域。一种新的通讯协议标准。能够理解成是:同时支持同源和跨域的Ajax。
关于Ajax请求,能够看本人的基础文章:Ajax入门和发送http请求
在回答 Ajax 的问题时,要回答如下几个方面:
一、XMLHttpRequest 的工做原理
二、兼容性处理
XMLHttpRequest只有在高级浏览器中才支持。在回答问题时,这个兼容性问题不要忽略。
三、事件的出发条件
四、事件的触发顺序
XMLHttpRequest有不少触发事件,每一个事件是怎么触发的。
(1)建立XMLHttpRequest 对象。
(2)使用open方法设置请求的参数。open(method, url, 是否异步)。
(3)发送请求。
(4)注册事件。 注册onreadystatechange事件,状态改变时就会调用。
若是要在数据完整请求回来的时候才调用,咱们须要手动写一些判断的逻辑。
(5)获取返回的数据,更新UI。
get请求举例:
<!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 () { // 发送ajax 请求 须要 五步 // (1)建立异步对象 var ajaxObj = new XMLHttpRequest(); // (2)设置请求的参数。包括:请求的方法、请求的url。 ajaxObj.open('get', '02-ajax.php'); // (3)发送请求 ajaxObj.send(); //(4)注册事件。 onreadystatechange事件,状态改变时就会调用。 //若是要在数据完整请求回来的时候才调用,咱们须要手动写一些判断的逻辑。 ajaxObj.onreadystatechange = function () { // 为了保证 数据 完整返回,咱们通常会判断 两个值 if (ajaxObj.readyState == 4 && ajaxObj.status == 200) { // 若是可以进到这个判断 说明 数据 完美的回来了,而且请求的页面是存在的 // 5.在注册的事件中 获取 返回的 内容 并修改页面的显示 console.log('数据返回成功'); // 数据是保存在 异步对象的 属性中 console.log(ajaxObj.responseText); // 修改页面的显示 document.querySelector('h1').innerHTML = ajaxObj.responseText; } } } </script> </body> </html>
post 请求举例:
<!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'); // 若是想要使用post提交数据,必须添加此行 xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); // 将数据经过send方法传递 xhr.send('name=fox&age=18'); // 发送并接受返回值 xhr.onreadystatechange = function () { // 这步为判断服务器是否正确响应 if (xhr.readyState == 4 && xhr.status == 200) { alert(xhr.responseText); } }; </script> </body> </html>
注册 onreadystatechange 事件后,每当 readyState 属性改变时,就会调用 onreadystatechange 函数。
readyState:(存有 XMLHttpRequest 的状态。从 0 到 4 发生变化)
0: 请求未初始化
1: 服务器链接已创建
2: 请求已接收
3: 请求处理中
4: 请求已完成,且响应已就绪
上图的参考连接:
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); } } }; }
Ajax 的推荐连接:http://www.javashuo.com/article/p-kiecxjqk-eu.html
方式以下:
一、JSONP
二、WebSocket
三、CORS
四、Hash
五、postMessage
上面这五种方式,在面试时,都要说出来。
面试会问:JSONP的原理是什么?怎么实现的?
在CORS和postMessage之前,咱们一直都是经过JSONP来作跨域通讯的。
JSONP的原理:经过<script>
标签的异步加载来实现的。好比说,实际开发中,咱们发现,head标签里,能够经过<script>
标签的src,里面放url,加载不少在线的插件。这就是用到了JSONP。
JSONP的实现:
好比说,客户端这样写:
<script src="http://www.smyhvae.com/?data=name&callback=myjsonp"></script>
上面的src中,data=name
是get请求的参数,myjsonp
是和后台约定好的函数名。
服务器端这样写:
myjsonp({ data: {} })
因而,本地要求建立一个myjsonp 的全局函数,才能将返回的数据执行出来。
实际开发中,前端的JSONP是这样实现的:
<script> var util = {}; //定义方法:动态建立 script 标签 /** * [function 在页面中注入js脚本] * @param {[type]} url [description] * @param {[type]} charset [description] * @return {[type]} [description] */ 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; }; /** * [function 处理jsonp] * @param {[type]} url [description] * @param {[type]} onsucess [description] * @param {[type]} onerror [description] * @param {[type]} charset [description] * @return {[type]} [description] */ 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标签 script.onload = script.onreadystatechange = function () { //监听加载成功的事件,获取数据 if (!script.readyState || /loaded|complete/.test(script.readyState)) { script.onload = script.onreadystatechange = null; // 移除该script的 DOM 对象 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); //往html中增长这个标签,目的是把请求发送出去 }; </script>
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.'); };
Websocket的推荐连接:http://www.ruanyifeng.com/blog/2017/05/websocket.html
CORS 能够理解成是既能够同步、也能够异步*的Ajax。
fetch 是一个比较新的API,用来实现CORS通讯。用法以下:
// url(必选),options(可选) fetch('/some/url/', { method: 'get', }).then(function (response) { //相似于 ES6中的promise }).catch(function (err) { // 出错了,等价于 then 的第二个参数,但这样更好用更直观 });
推荐连接里有详细的配置。
另外,若是面试官问:“CORS为何支持跨域的通讯?”
答案:跨域时,浏览器会拦截Ajax请求,并在http头中加Origin。
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; };
H5中新增的postMessage()方法,能够用来作跨域通讯。既然是H5中新增的,那就必定要提到。
场景:窗口 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);
想学习代码以外的软技能?不妨关注个人微信公众号:生命团队(id:vitateam
)。
扫一扫,你将发现另外一个全新的世界,而这将是一场美丽的意外: