1. 聊聊你对跨域的理解
前端开发进化史
服务器渲染 「半服务器渲染 SSR」

客户端渲染

为啥会产生跨域
服务器分离:WEB服务器、数据服务器、图片服务器…
云信息共享:第三方API接口
有助于分离开发:开发跨域、部署同源
……
解决方案
JSONP
CORS
Proxy
……
同源策略请求 && 非同源策略(跨域)请求
HTML页面预览的地址:http://127.0.0.1:5500/index.html
基于Ajax发送请求的数据接口地址:http://127.0.0.1:9999/user/list
如果两个地址的“协议、域名、端口号”完全一致,则是同源请求,只要有一个不一样,则就是跨域请求!!
+ 同源请求:把web页面和后台的接口程序部署在“相同服务器的相同服务下”
+ 跨域请求:页面和后台分开部署「同一台服务器的不同端口下、或者不同服务器下...」
项目场景:
+ 开发的时候:我们在自己的电脑上启动服务预览项目,但是后台的程序在其他的服务器上,没有在一起,此时的数据请求就是“跨域”的!!
+ 部署到服务器:
+ 全部署在一起(相同服务器的相同服务下),此时就是同源「不建议」
+ 当代部署一般都是分服务器部署,也就是页面和后台是部署在不同服务器上的,此时就是跨域
+ ...
默认情况下不允许Ajax跨域请求,因为浏览器有“安全策略”
Access to XMLHttpRequest at 'https://www.jianshu.com/asimov...' from origin 'http://127.0.0.1:5500' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
服务器和服务器之间默认是没有跨域限制的
==============================
非同源策略(跨域)请求方案:
+ proxy跨域代理:原理就是利用“服务器和服务器之间不存在跨域”来实现的
步骤:
@1 在本地启动一个服务:一方面可以实现页面的预览,另一方面也可以代理转发数据请求
@2 我们以后再发数据请求,发送给本地启动的这个服务,而这个服务会去真正的服务器上获取到相关的数据,再返回给客户端!!
+ 本地开发的时候,我们基于proxy跨域代理来完成「webpack-dev-server、Node...」
+ 部署到服务器的时候,我们一般是基于nginx的反向代理来处理
+ CORS跨域资源共享:默认因为浏览器的安全策略,是不允许ajax跨域访问的,但是如果服务器设置了 Access-Control-Allow-Origin 响应头信息,设置允许这个源发送请求,那么浏览器也就不在去限制了!!
步骤:
@1 由服务器设置允许源
Access-Control-Allow-Origin 设置允许的源「白名单机制」
Access-Control-Allow-Credentials 是否允许携带资源凭证「例如:cookie」
服务器端设置了允许,则客户端也要配合设置允许
axios.defaults.withCredentials = true;
xhr.withCredentials=true;
Access-Control-Allow-Headers 设置允许的请求头
Access-Control-Allow-Methods 设置允许的请求方式
@2 在CORS跨域共享中,客户端首先会尝试发送一个OPTIONS试探请求,验证是否连接成功,连接成功后,再发送真正的请求!!
+ JSONP跨域解决方案
原理:利用了
服务器端:
- /*-CREATE SERVER-*/
- const express = require('express'),
- app = express();
- app.listen(1001, () => console.log(`服务启动成功,正在监听1001端口!`));
-
- app.get('/user/list', (req, res) => {
- // 获取传递进来的callback值,例如:'func'
- let { callback } = req.query;
- // 准备数据
- let result = {
- code: 0,
- data: ['张三', '李四']
- };
- // 返回给客户端指定的格式,例如:’函数名(数据)‘
- res.send(`${callback}(${JSON.stringify(result)})`);
- });
-
- /* STATIC WEB */
- app.use(express.static('./'));
- /*-CREATE SERVER-*/
- const express = require('express'),
- request = require('request'),
- app = express();
- app.listen(1001, () => console.log(`服务启动成功,正在监听1001端口!`));
-
- // 服务器接收到客户端发送过来的请求
- app.get('/aaa', (req, res) => {
- // 向简书发送相同请求,从简书服务器获取想要的数据「不存在域的限制的」
- let jianURL = `https://www.jianshu.com/asimov/subscriptions/recommended_collections`;
- req.pipe(request(jianURL)).pipe(res);
- });
-
- /* STATIC WEB */
- app.use(express.static('./'));
- /*-CREATE SERVER-*/
- const express = require('express'),
- app = express();
- app.listen(1001, () => console.log(`服务启动成功,正在监听1001端口!`));
-
- /*-MIDDLE WARE-*/
- // 设置白名单
- let safeList = ["http://127.0.0.1:5500", "http://127.0.0.1:3000", "http://127.0.0.1:8080", "http://127.0.0.1:5501"];
- app.use((req, res, next) => {
- let origin = req.headers.origin || req.headers.referer || "";
- origin = origin.replace(/\/$/g, '');
- origin = !safeList.includes(origin) ? '' : origin;
- res.header("Access-Control-Allow-Origin", origin);
- res.header("Access-Control-Allow-Credentials", true);
- res.header("Access-Control-Allow-Headers", "Content-Type,Content-Length,Authorization, Accept,X-Requested-With");
- res.header("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS,HEAD");
- req.method === 'OPTIONS' ? res.send('OK') : next();
- });
-
- /*-API-*/
- app.get('/list', (_, res) => {
- res.send({
- code: 0,
- message: 'zhufeng'
- });
- });
-
- /* STATIC WEB */
- app.use(express.static('./'));
webpack-dev-server
- const ENV = process.env.NODE_ENV;
- module.exports = {
- lintOnSave: ENV !== 'production',
- publicPath: './',
- productionSourceMap: false,
- // 对webpack-dev-server的配置
- devServer: {
- // 配置跨域代理「可以配置对多台服务器的代理」
- proxy: {
- // 所有以“/api”开始的请求,都发送到代理服务器「走这个配置」
- "/api": {
- target: "https://www.jianshu.com/asimov",
- ws: true,
- changeOrigin: true,
- pathRewrite: {
- "^/api": ""
- }
- },
- // 所有以“/zhihu”开始的请求,都代理到知乎服务器
- "/zhihu": {
- target: "https://news-at.zhihu.com/api/4",
- ws: true,
- changeOrigin: true,
- pathRewrite: {
- "^/zhihu": ""
- }
- }
- }
- }
- };
部署到服务器上:基于nginx实现反向代理

处理原理

自己基于node实现
const request = require('request');
app.get('/subscriptions/recommended_collections', function (req, res) {
let url = 'https://www.jianshu.com/asimov' + req.url;
req.pipe(request(url)).pipe(res);
});
扩展:其他跨域方案「配合iframe」
postMessage
window.name
document.domin
location.hash
……
----------------------------------------