面試必考:全面解析跨域及其解決方案
跨域問題是前端開發中常見且必須掌握的知識點之一。本文將詳細介紹跨域的概念、手寫JSONP和CORS跨域代碼及其原理,如何在Vue3項目中替換Mock數據接口為真實后端數據接口,以及總結九種常見的跨域解決方案。
一、什么是跨域?
跨域是指瀏覽器因同源策略的限制,無法訪問不同源(協議、域名、端口任一不同)的資源。例如,前端頁面運行在 http://example.com,但需要訪問 http://api.example.com 的數據時,就會遇到跨域問題。
二、手寫JSONP跨域代碼及原理講解
原理
JSONP(JSON with Padding)是一種非正式的數據傳輸格式,它通過 <script> 標簽的 src 屬性來實現跨域請求。因為 <script> 標簽不受同源策略限制。
實現代碼
前端代碼:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>JSONP Example</title> </head> <body> <h1>JSONP Cross-Origin Request</h1> <button onclick="fetchData()">Fetch Data</button> <script> function fetchData() { const script = document.createElement('script'); script.src = 'http://localhost:3000/data?callback=handleResponse'; document.body.appendChild(script); } function handleResponse(data) { console.log('Received data:', data); } </script> </body> </html>
后端代碼(Node.js示例):
const express = require('express'); const app = express(); const port = 3000; app.get('/data', (req, res) => { const callback = req.query.callback; const data = { message: 'Hello, JSONP!' }; res.send(`${callback}(${JSON.stringify(data)})`); }); app.listen(port, () => { console.log(`Server running at http://localhost:${port}`); });
三、手寫跨域資源共享(CORS)處理跨域及原理
原理
CORS(Cross-Origin Resource Sharing)是一種機制,它使用額外的HTTP頭來告訴瀏覽器,允許從其他域加載資源。服務器通過設置適當的HTTP響應頭,來告訴瀏覽器哪些域可以訪問資源。
實現代碼
前端代碼:
fetch('http://localhost:3000/data', { method: 'GET', headers: { 'Content-Type': 'application/json' } }) .then(response => response.json()) .then(data => console.log('Received data:', data)) .catch(error => console.error('Error:', error));
后端代碼(Node.js示例):
const express = require('express'); const app = express(); const port = 3000; app.use((req, res, next) => { res.header('Access-Control-Allow-Origin', '*'); // 允許所有域訪問 res.header('Access-Control-Allow-Methods', 'GET, POST'); // 允許的方法 res.header('Access-Control-Allow-Headers', 'Content-Type'); // 允許的請求頭 next(); }); app.get('/data', (req, res) => { res.json({ message: 'Hello, CORS!' }); }); app.listen(port, () => { console.log(`Server running at http://localhost:${port}`); });
四、Vue3通用后臺管理如何替換Mock數據接口為真實的后端數據接口
在Vue3項目中,我們可以通過修改API請求地址,將Mock數據接口替換為真實后端數據接口。
示例
假設我們有一個Vue3項目,之前使用Mock數據:
// api.js (使用Mock數據) export const getData = () => { return new Promise((resolve) => { resolve({ data: 'Mock data' }); }); };
現在,我們將其替換為真實后端接口:
// api.js (使用真實后端數據) import axios from 'axios'; const BASE_URL = 'http://localhost:3000'; export const getData = () => { return axios.get(`${BASE_URL}/data`); };
然后在組件中調用:
<template> <div> <button @click="fetchData">Fetch Data</button> <p>{{ data }}</p> </div> </template> <script> import { getData } from './api'; export default { data() { return { data: '' }; }, methods: { fetchData() { getData() .then(response => { this.data = response.data.message; }) .catch(error => { console.error('Error:', error); }); } } }; </script>
五、九種跨域方案的匯總
- JSONP:通過
<script>標簽實現跨域,只支持GET請求。 - CORS:通過設置HTTP頭允許跨域請求,支持復雜請求。
- 服務器代理:如使用Node.js中間件(http-proxy-middleware)或Nginx代理請求,繞過瀏覽器的同源策略。
- WebSocket:WebSocket協議不受同源策略限制,可以實現跨域通信。
- PostMessage:通過
window.postMessage實現不同窗口間的數據傳遞。 - 跨域資源嵌入:通過
<iframe>、<img>、<link>、<script>等標簽加載跨域資源。 - document.domain:適用于主域相同子域不同的跨域,通過設置相同的
document.domain實現。 - window.name:通過改變窗口的
name屬性實現跨域數據傳遞。 - 跨域請求偽造(CORS Preflight):通過簡單請求或預檢請求繞過CORS限制。
1. JSONP示例
詳見上文。
2. CORS示例
詳見上文。
3. 服務器代理示例(Node.js中間件)
const { createProxyMiddleware } = require('http-proxy-middleware');
const express = require('express');
const app = express();
app.use('/api', createProxyMiddleware({
target: 'http://backend-server.com',
changeOrigin: true,
}));
app.listen(3000);
4. WebSocket示例
const socket = new WebSocket('ws://localhost:3000'); socket.onopen = () => { console.log('WebSocket connection opened'); socket.send('Hello Server!'); }; socket.onmessage = (event) => { console.log('Received:', event.data); };
后端代碼(Node.js示例):
const WebSocket = require('ws'); const server = new WebSocket.Server({ port: 3000 }); server.on('connection', ws => { ws.on('message', message => { console.log('Received:', message); ws.send('Hello Client!'); }); });
5. PostMessage示例
<!-- parent.html --> <iframe id="child" src="child.html" style="display:none;"></iframe> <script> const child = document.getElementById('child').contentWindow; child.postMessage('Hello from parent!', 'http://child.com'); window.addEventListener('message', (event) => { if (event.origin === 'http://child.com') { console.log('Received from child:', event.data); } }); </script> <!-- child.html --> <script> window.addEventListener('message', (event) => { if (event.origin === 'http://parent.com') { console.log('Received from parent:', event.data); event.source.postMessage('Hello from child!', event.origin); } }); </script>
6. 跨域資源嵌入示例
<!-- index.html --> <script src="http://cross-origin.com/script.js"></script>
7. document.domain示例
<!-- parent.example.com --> <script> document.domain = 'example.com'; // 訪問子域內容 </script>
8. window.name示例
<!-- page1.html --> <script> window.name = 'data from page1'; location.href = 'http://cross-origin.com/page2.html'; </script> <!-- page2.html --> <script> console.log(window.name); // 'data from page1' </script>
9. 跨域請求偽造(CORS Preflight)示例
后端代碼:
const express = require('express'); const app = express(); app.use((req, res, next) => { res.header('Access-Control-Allow-Origin', '*'); res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS'); res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization'); if (req.method === 'OPTIONS') { return res.sendStatus(204); // 對預檢請求直接返回204狀態碼 } next(); }); app.post('/api', (req, res) => { res.json({ message: 'Data received' }); }); app.listen(3000, () => { console.log('Server running at http://localhost:3000'); });

浙公網安備 33010602011771號