面试题-前端如何解决跨域

回答模版

1、解释跨域原因:浏览器的同源策略 限制,协议、域名、端口 只要有一个不同都会出现跨域

2、场景的解决方案:CORS、http代理、JSONP等

实际项目中在生产环境是后端配置CORS,前端无需特殊处理,开发环境前端用http proxy解决,无需等待后端支持。

服务端CORS的优势
  • 浏览器原生支持:无需额外配置,浏览器自动处理
  • 安全性可控:服务端精确控制允许的源、方法、头部

1.CORS( Cross-Origin Resource Sharing ) 跨域资源共享

服务端解决:通过配置一些响应头信息 Access-Control-Allow-Origin:必选

所有的cors请求都会预先发一个options请求 发送真正请求前先发一个试探性请求;options请求是跨域请求之前的预检查,是浏览器自动发起的,返回头信息里可以告知我们哪些方法可以用

缺点是允许源只能写一个地址 或者直接写* 写*不安全并且不允许携带cookie了

2. 开发环境代理 http proxy

webpack 配置 webpack-dev-server配置 (node启了一个本地服务器)

javascript 复制代码
 devServer: {
      proxy: {
        '/api': {
          target: 'http://localhost:8080',
          changeOrigin: true,
          pathRewrite: {
            '^/api': ''
          }
        }
      }
    }

访问的时候使用/api/xx/xx...

3.Jsonp

jsonp的原理就是利用<script>标签没有跨域限制,通过<script>标签src属性,将本地的全局函数通过callback传到服务器,服务端将接口返回数据拼凑到callback函数中,返回给客服端

实现思路

服务端的代码:

javascript 复制代码
let express = require('express');
app = express();
app.listen('8001', () => {
  console.log('ok');
})
app.get('/list', (req,res) => {
  let { callback } = req.query;
  let data = {
    code: 0,
    message:'发送的数据'
  }
  res.send(`${callback}(${JSON.stringify(data)}`)
})

客户端代码

javascript 复制代码
<script>

    var script = document.createElement('script');
    script.type = 'text/javascript';
    // 传参一个回调函数名给后端,方便后端返回时执行这个在前端定义的回调函数
    script.src = 'http://www.domain2.com:8080/login?user=admin&callback=handleCallback';
    document.head.appendChild(script);
    // 回调执行函数
    function handleCallback(res) {
        alert(JSON.stringify(res));
    }
</script>

jsonp的缺点:只支持get请求

4.nginx反向代理

通过 Nginx 配置反向代理,将跨域请求转发到同源接口,从而避免浏览器的同源策略限制。

javascript 复制代码
server {
  listen 80;
  server_name your-domain.com;

  location /api {
    # 设置代理目标地址
    proxy_pass http://api.example.com;
    
    # 设置允许的跨域请求头
    add_header Access-Control-Allow-Origin $http_origin;
    add_header Access-Control-Allow-Methods "GET, POST, OPTIONS";
    add_header Access-Control-Allow-Credentials true;
    add_header Access-Control-Allow-Headers "Origin, X-Requested-With, Content-Type, Accept";
    
    # 处理预检请求(OPTIONS 请求)
    if ($request_method = OPTIONS) {
      return 200;
    }
  }
}

这样,当你在前端发送请求到 /api 路径时,Nginx 会将请求代到 http://api.example.com

5.Window.postMessage

实现跨源通信,两个不同页面的脚本实现通信

A页面

html 复制代码
<body>
  <iframe id="iframe" style="display:none" src="http://127.0.0.1:1002/MESSAGE/b.html"></iframe>
  <script>
    iframe.onload=function(){
      iframe.contentWindow.postMessage('发送的数据','http://127.0.0.1:1002')
    }
    // 监听b页面传过来的数据
    window.onmessage = function(ev){
      console.log(ev.data)
    }
  </script>
</body>

B页面

6. WebSocket协议跨域

WebSocket protocol是HTML5一种新的协议。它实现了浏览器与服务器全双工通信,同时允许跨域通讯

如果想完全搭建一个 WebSocket 服务端比较麻烦,又浪费时间。所以一般会使用Socket.io 这个库,

简单实现一个聊天功能:

服务端:

javascript 复制代码
let express = require('express');
let app = express();
let server = require('http').createServer(app);
let io = require('socket.io')(server);
let path = require('path');

app.use('/', (req, res, next) => {
  res.status(200).sendFile(path.resolve(__dirname, 'index.html'));
});

// 开启 socket.io
io.on('connection', (client) => {

  // 如果有新客户端进来,显示 ID
  console.log(`客户端 ID:${client.id}`);

  // 监听客户端的输入信息
  client.on('channel', (data) => {
    console.log(`客户端 ${client.id} 发送信息 ${data}`);
    io.emit('broadcast', data);
  });

  // 判断客户端是否关闭
  client.on('disconnect', () => {
    console.log(`客户端关闭:${client.id}`);
  });
});

server.listen(3000, () => {
  console.log('服务监听 3000 端口');
});

客户端:

html 复制代码
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Socket.io</title>
  <script src="https://cdn.bootcss.com/socket.io/2.2.0/socket.io.slim.js"></script>
</head>

<body>

  <input type="text" id="input">
  <button id="btn">send</button>
  <div id="content-wrap"></div>

  <script>
    window.onload = function () {
      let inputValue = null;

      // 连接 socket.io
      let socket = io('http://localhost:3000');
      // 将创建的信息以添加 p 标签的形式展示成列表
      socket.on('broadcast', data => {
        let content = document.createElement('p');
        content.innerHTML = data;
        document.querySelector('#content-wrap').appendChild(content);
      })

      // 设置输入框的内容
      let inputChangeHandle = (ev) => {
        inputValue = ev.target.value;
      }
      // 获取输入框并监听输入
      let inputDom = document.querySelector("#input");
      inputDom.addEventListener('input', inputChangeHandle, false);

      // 当用户点击发送信息的时候,进行数据交互
      let sendHandle = () => {
        socket.emit('channel', inputValue);
      }
      let btnDom = document.querySelector("#btn");
      btnDom.addEventListener('click', sendHandle, false);

      // 打页面卸载的时候,通知服务器关闭
      window.onunload = () => {
        btnDom.removeEventListener('click', sendHandle, false);
        inputDom.removeEventListener('input', inputChangeHandle, false);
      }
    };
  </script>
</body>

</html>

用on监听,用emit发送一个指令。

相关推荐
Sailing4 小时前
👉 👉 Vue3 自定义 Hook:从入门到进阶(~~安静的阅读2分钟,相信我,这篇文章一定能给你启发)
前端·javascript·vue.js
南雨北斗4 小时前
Vue3 v-html的用法
前端
爱学习的茄子4 小时前
Function Call:让AI从文本生成走向智能交互的技术革命
前端·深度学习·openai
aol1214 小时前
X6官方示例「数据加工DAG图」转为Vue版
前端·vue.js
南雨北斗4 小时前
vue3 attribute绑定
前端
一枚前端小能手4 小时前
🚀 主线程卡死用户要骂娘?Web Worker让你的应用丝滑如德芙
前端·javascript
小桥风满袖4 小时前
极简三分钟ES6 - Promise
前端·javascript
breeze_whisper4 小时前
当前端收到一个比梦想还大的数字:BigInt处理指南
前端·面试
小喷友4 小时前
阶段四:实战(项目开发能力)
前端·rust