1. 前后端分离发展史
1.1前后端不分离
- 早期前后端不分离,两者共同操作jsp文件,前端写样式+特效,后端处理数据动态更新;
- 一个页面前端需要等待后端有数据,后端需要等待前端由html结构,很浪费时间;
1.2 前后端分离
- 后端接到指令,调用请求方法查询数据并响应给前端;
- 前端书写样式及特效,展示从后端获取的数据;
- 前后端分离后,浏览器考虑到数据的安全性,进行了同源策略限制,这就导致了跨域问题的出现;
1.3 同源策略和跨域的产生
- 同源策略:是一个重要的安全策略,它用于限制一个源的文档或者它加载的脚本如何能与另一个源的资源进行交互;
- 通俗的说就是对网站A中的接口访问网站B中的数据时的一种限制,只有两个网站满足同源策略才可以互相访问;
- 协议、域名、端口全部一致才满足同源策略;
- 跨域:是指浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,协议、域名、端口有一项不一致就会造成跨域问题;
2. 跨域详解(跨域问题的解决)
跨域问题常见的解决方案有:
- jsonp(json with padding)
- CORS (CROSS-Origin Resource Sharing 跨域资源共享)
- 代理(proxy)
- webpack代理
2.1 jsonp(json with padding)
- 定义:json with padding 将json 数据作为填充,简单来说就是服务器将json数据作为函数的参数;
- JSONP 是一种处理跨域的方式,和json没有多大关系(只是其数据传输是json数据);
- 利用外链非ajax不受同源策略影响,实现站外js执行,并且传递数据;
- 只能是get请求,前端和后端都需要coding;
- 常见的外链标签img(jpg) script(js) link(css) iframe(html),其中最常用的是script(js)标签,因为可以外链js代码实现逻辑处理;
2.1.1 实现的步骤及代码
- 封装一个myJsonP函数,接收参数url和fn(回调函数);
- 动态创建script标签,并定义动态的方法名;
- 设置src(拼接完整的get请求的url),并在window上声明方法名;
- 动态创建的script标签将插入到文档中;
- 使用完毕 释放资源;
function myJsonP(url, fn) {
// 1. 动态创建script标签
let script = document.createElement('script');
// 1.5 定义动态的方法名
let fnName = 'callbackName_' + Date.now();
// 2. 设置src
script.src = url + '?callback=' + fnName; // get请求
// 2.5 声明方法名
window[fnName] = function (data) {
// 4. 使用完毕 释放资源
script.remove();
delete window[fnName];
fn(data);
}
// 3. 插入到文档中
document.body.appendChild(script);
}
附一个Promise封装版的实现代码(这里就不需要传入回调函数了,直接respse()调用即可)
function myJsonP_promise(url) {
return new Promise((res, rej) => {
// 1. 动态创建script标签
let script = document.createElement('script');
// 1.5 定义动态的方法名
let fnName = 'callbackName_' + Date.now();
// 2. 设置src
script.src = url + '?callback=' + fnName; // get请求
// 2.5 声明方法名
window[fnName] = function (data) {
// 4. 使用完毕 释放资源
script.remove();
delete window[fnName];
res(data);
}
// 3. 插入到文档中
document.body.appendChild(script);
});
}
2.2 CORS (CROSS-Origin Resource Sharing 跨域资源共享)
- 原理:服务器设置(通俗理解成,是后端让浏览器不要多管闲事);
- 同源政策默认阻止跨域获取资源,但是CORS给了web服务器权限,即服务器可以选择,允许访问他们的资源;
- 方法就是设置三个响应头(Access-Control-Allow-Origin、Access-Control-Allow-Methods、Access-Control-Allow-Headers);
- Access-Control-Allow-Origin:指定了哪些域名或ip地址可以跨域也可以写* 代表允许所有地址
// 1. Access-Control-Allow-Origin
res.setHeater('Access-Control-Allow-Origin',' http://XXX.cn || * || ip地址');
- Access-Control-Allow-Methods: CORS默认支持9个请求头,如果在9个请求头之外 要单独设置,多个请求头之间要用英文逗号隔开;
res.setHeater('Access-Control-Allow-Headers','Content-Type,X-Custom-Header');
- Access-Control-Allow-Headers:默认情况下CORS仅支持客户端发起的GET、POST、HEAD请求,如果希望客户端发送PUT、DELETE请求需要在服务器端设置;
res.setHeater('Access-Control-Allow-Method','GET');
- 若要允许携带cookie: 设置Access-Control-Allow-Credentials:true
- 补充知识(简单请求和非简单请求)
- 根据头信息和请求方式,浏览器进行了优化,对请求进行划分,分为简单请求和非简单请求;
- 简单请求:满足浏览器请求会直接发送并在请求头中携带Origin表示本次来自哪个源;
- 如果不满足简单请求,会在发送请求之前发送一个预检请求大小为0kb 请求方式为options收到服务器响应后,分析它是否支持跨域,如果支持跨域则直接发送,如果不支持报错!
2.3 代理(proxy)
- 原理:由当前页面的服务器帮助我们转发,不存在跨域之说(主要在后端实现,提供给前端一个跨域接口);
- 正向代理和反向代理
- 正向代理:我们常说的代理服务器(多指 VPN),一般就是正向代理。它的特点是隐藏了真实的请求客户端,服务端不知道真实的客户端是谁,客户端请求的服务都由代理服务器代替来请求;
- 反向代理:反向代理隐藏了真实的服务端,当我们请求www.baidu.com 的时候,就像拨打 QQ 客服热线一样,背后可能有成千上万台服务器为我们服务,但具体是哪一台,你不知道,也不需要知道;
2.4 webpack代理
通过服务器代理去发ajax,这个代理是webpack提供给我们的,因为服务器只是对ajax的同源限制,并不会限制服务器之间的通信;
//vue.config.js
devServer: {
proxy: {
// 如果请求地址以/api打头,就出触发代理机制
// http://localhost:8080/api/login -> http://localhost:3000/api/login
'/api': {
target: 'http://localhost:3000' // 我们要代理的真实接口地址
}
}
}