前端跨域的方式是面试八股文中常见的一部分,最近面试中也是被问到跨域的问题,跨域的解决方案无非就是几个
本地项目中调试用的最多的就是 node代理
浏览器从一个网页去请求另一个资源时,域名、端口、协议任一不同,都是跨域。在前后端分离的模式下,前后端的域名是不一致的,跨域是必然发生的事情。但服务器与服务器之间请求数据并不会存在跨域行为,跨域行为是浏览器安全策略限制,也称为浏览器的同源策略
如果是跨域,你将会发现下图的报错,经典的跨域问题,前端和后端都是我的本地项目,但仅仅是端口的不同,浏览器请求就发生了跨域的问题
Access to XMLHttpRequest at 'http://127.0.0.1:8888/' from origin 'http://127.0.0.1:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
由于需要用到一个后端接口来测试是否能够发生跨域效果,我们利用node开启一个服务,在vue项目之外开启一个node文件,然后执行以下代码
//node1.js
var http = require("http");
http.createServer(function (request, response) {
// 发送 HTTP 头部
// HTTP 状态值: 200 : OK
// 内容类型: text/plain
response.writeHead(200, {'Content-Type': 'text/plain'});
// 发送响应数据 "Hello World"
response.end('Hello World\n');
}).listen(8888);
// 终端打印如下信息
console.log('Server running at http://127.0.0.1:8888/');
vscode中可以直接下载一个工具用来跑node文件,直接右键就能运行,非常的方便
如果没有安装的同学可以执行命令 node node1.js 进行执行,服务开启后成功如图
我们再在浏览器打开服务端口进行查看返回的结果测试一下
到这里我们的后端node服务已经能够启动了,那就说明可以当做一个接口来进行调用然后测试我们的跨域了
首先我们要发送请求,所以我们需要利用到ajax,这里引入axios太麻烦,所以直接用原生的ajax来发送请求,顺便学习一下如何手写一个原生的ajax请求
function ajax(options) {
//创建XMLHttpRequest对象
const xhr = new XMLHttpRequest()
//初始化参数的内容
options = options || {}
options.type = (options.type || 'GET').toUpperCase()
options.dataType = options.dataType || 'json'
const params = options.data
//发送请求
if (options.type === 'GET') {
xhr.open('GET', options.url + '?' + params, true)
xhr.send(null)
} else if (options.type === 'POST') {
xhr.open('POST', options.url, true)
xhr.send(params)
}
//接收请求
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
let status = xhr.status
if (status >= 200 && status < 300) {
options.success && options.success(xhr.responseText, xhr.responseXML)
} else {
options.fail && options.fail(status)
}
}
}
}
对我们的ajax请求进行一个简单的封装,这样就算是一个比较简单的手写的ajax,从request对象的创建到参数初始化,发送请求及接受相应都比较完整
在我们的vue项目中执行一个todo函数,放置我们请求的ajax函数,对应我们刚才启动的node服务进行请求,测试是否会发生跨域问题
function todo(){
ajax({
type: 'post',
dataType: 'json',
data: {},
url: 'http://127.0.0.1:8888',
// url: '/api',
success: function(text,xml){//请求成功后的回调函数
console.log(text)
},
fail: function(status){请求失败后的回调函数
console.log(status)
}
})
}
对函数执行后发现无可置疑,肯定是发生了跨域
由于端口的不同, vue项目跑在了3000端口,mode服务跑在了8888端口,所以发生了跨域的问题
这是配置代理跨域前的请求,记住这个格式待会和代理成功的做一个对比
由于项目中使用的是vue3+vite包,所以我们打开vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import styleImport, { VantResolve } from 'vite-plugin-style-import';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
styleImport({
resolves: [VantResolve()],
}),],
server: { //主要是加上这段代码
host: '127.0.0.1',
port: 3000,
proxy: {
'/api': {
target: 'http://127.0.0.1:8888', //实际请求地址
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '')
},
}
}
})
这个是代理成功的请求会是这个样子,我们发送的127.0.0.1:8888变成了127.0.0.1:3000,所以我们的代理生效了,不发跨域了,因为我们请求的和本地vue项目没有发送同源策略(请求地址和端口都是相同的),但是为什么我们请求成功了呢?
我们要请求的明明是8888端口才对呀?让我们看下面的解析
从上图中,server是我们新增的部分,我们每一个配置都来解析一下
如果不加这段配置的话,我们npm run dev的时候vue项目就不会跑在127.0.0.1:3000上,而是会自动分配一个地址和端口
意思就是识别到请求 ‘api/’ 就会变成 http://127.0.0.1:8888’
我们要调用的接口是 http://127.0.0.1:8888
但我们的项目是在127.0.0.1:3000所以proxy会做如下代理进行转发
127.0.0.1:3000/api/ -> http://127.0.0.1:8888
如果还有后续地址的话 127.0.0.1:3000/api/test/test1 -> http://127.0.0.1::8888/test/test1
proxy其实就是因为浏览器同源协议无法请求非同源的地址,但是服务器直接没有同源协议,利用将本地请求转到本地服务器进行代理转发,从而绕过了同源协议的限制,通过代理的实现可以解决跨域的问题,换了个思路去解决实际产生的问题,实在是一个比较好的解决方法
参考文章