本文最初发表于博客园, 并在 GitHub 上持续更新前端的系列文章欢迎在 GitHub 上关注我, 一起入门和进阶前端
以下是正文
前言
前端通信类的问题, 主要包括以下内容:
1 什么是同源策略及限制
同源策略是一个概念, 就一句话有什么限制, 就三句话能说出来即可
2 前后端如何通信
如果你不准备, 估计也就只能说出 ajax
3 如何创建 Ajax
Ajax 在前后端通信中经常用到做业务时, 可以借助第三方的库, 比如 vue 框架里的库 jQuery 也有封装好的方法但如果让你用原生的 js 去实现, 该怎么做?
这就是考察你的动手能力, 以及框架原理的掌握如果能写出来, 可以体现出你的基本功
4 跨域通信的几种方式
这部分非常重要无非就是问你: 什么是跨域跨域有什么限制跨域有几种方式
下面分别讲解
同源策略的概念和具体限制
同源策略: 限制从一个源加载的文档或脚本如何与来自另一个源的资源进行交互这是一个用于隔离潜在恶意文件的关键的安全机制(来自 MDN 官方的解释)
具体解释:
(1)源包括三个部分: 协议域名端口 (http 协议的默认端口是 80) 如果有任何一个部分不同, 则源不同, 那就是跨域了
(2)限制: 这个源的文档没有权利去操作另一个源的文档这个限制体现在:(要记住)
CookieLocalStorage 和 IndexDB 无法获取
无法获取和操作 DOM
不能发送 Ajax 请求我们要注意, Ajax 只适合同源的通信
前后端如何通信
主要有以下几种方式:
Ajax: 不支持跨域
webSocket: 不受同源策略的限制, 支持跨域
CORS: 不受同源策略的限制, 支持跨域一种新的通信协议标准可以理解成是: 同时支持同源和跨域的 Ajax
如何创建 Ajax
关于 Ajax 请求, 可以看本人的基础文章: Ajax 入门和发送 http 请求
在回答 Ajax 的问题时, 要回答以下几个方面:
1XMLHttpRequest 的工作原理
2 兼容性处理
XMLHttpRequest 只有在高级浏览器中才支持在回答问题时, 这个兼容性问题不要忽略
3 事件的出发条件
4 事件的触发顺序
XMLHttpRequest 有很多触发事件, 每个事件是怎么触发的
发送 Ajax 请求的五个步骤(XMLHttpRequest 的工作原理)
(1)创建 XMLHttpRequest 对象
(2)使用 open 方法设置请求的参数 open(method, url, 是否异步)
(3)发送请求
(4)注册事件 注册 onreadystatechange 事件, 状态改变时就会调用
如果要在数据完整请求回来的时候才调用, 我们需要手动写一些判断的逻辑
(5)获取返回的数据, 更新 UI
发送 get 请求和 post 请求
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 事件
注册 onreadystatechange 事件后, 每当 readyState 属性改变时, 就会调用 onreadystatechange 函数
readyState:(存有 XMLHttpRequest 的状态从 0 到 4 发生变化)
0: 请求未初始化
1: 服务器连接已建立
2: 请求已接收
3: 请求处理中
4: 请求已完成, 且响应已就绪
事件的触发条件
事件的触发顺序
上图的参考链接:
你真的会使用 XMLHttpRequest 吗?
实际开发中用的 原生 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);
- }
- }
- };
- }
Ajax 的推荐链接: https://segmentfault.com/a/1190000006669043
跨域通信的几种方式
方式如下:
- JSONP
- WebSocket
- CORS
- Hash
- postMessage
上面这五种方式, 在面试时, 都要说出来
1JSONP
面试会问: 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>
- 2WebSocket
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
3CORS
CORS 可以理解成是既可以同步也可以异步 * 的 Ajax
fetch 是一个比较新的 API, 用来实现 CORS 通信用法如下:
- // url(必选),options(可选)
- fetch('/some/url/', {
- method: 'get',
- }).then(function (response) { // 类似于 ES6 中的 promise
- }).catch(function (err) {
- // 出错了, 等价于 then 的第二个参数, 但这样更好用更直观
- });
CORS 的推荐链接: http://www.ruanyifeng.com/blog/2016/04/cors.html
推荐链接里有详细的配置
另外, 如果面试官问: CORS 为什么支持跨域的通信?
答案: 跨域时, 浏览器会拦截 Ajax 请求, 并在 http 头中加 Origin
4Hash
url 的 #后面的内容就叫 HashHash 的改变, 页面不会刷新这就是用 Hash 做跨域通信的基本原理
补充: url 的? 后面的内容叫 SearchSearch 的改变, 会导致页面刷新, 因此不能做跨域通信
使用举例:
场景: 我的页面 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;
- };
5postMessage()方法
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) { // 这里强调的是 B 窗口里的 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