公众号:小博的前端笔记
一、主流解决方案详解
1. CORS (跨域资源共享)
-
原理:服务器设置响应头告知浏览器允许跨域请求。
-
关键响应头:
yamlAccess-Control-Allow-Origin: * // 或指定域名(如 https://example.com) Access-Control-Allow-Methods: GET, POST, PUT Access-Control-Allow-Headers: Content-Type, Authorization Access-Control-Allow-Credentials: true // 允许发送Cookie Access-Control-Max-Age: 86400 // 预检请求缓存时间
-
请求分类:
- 简单请求 :直接发送(GET/POST/HEAD + 特定Header +
application/x-www-form-urlencoded
/multipart/form-data
/text/plain
)。 - 预检请求 (Preflight) :非简单请求先发
OPTIONS
请求询问服务器(面试常考!)。
- 简单请求 :直接发送(GET/POST/HEAD + 特定Header +
-
代码示例:
php// 前端(需带凭证时) fetch('https://api.target.com/data', { credentials: 'include' });
rust// Node.js服务器设置 res.setHeader('Access-Control-Allow-Origin', 'https://your-domain.com'); res.setHeader('Access-Control-Allow-Credentials', 'true');
2. JSONP (JSON with Padding) (历史方案)
-
原理 :利用
<script>
标签无跨域限制的特性。 -
特点:
- 仅支持GET请求
- 需服务器配合返回函数调用(如
callbackName({data})
)
-
代码示例:
xml<script> function handleResponse(data) { console.log(data); } </script> <script src="https://api.target.com/data?callback=handleResponse"></script>
3. WebSocket
-
原理:WebSocket协议本身支持跨域(建立连接时通过HTTP协商升级协议)。
-
代码示例:
iniconst socket = new WebSocket('ws://target.com'); socket.onmessage = (event) => { console.log(JSON.parse(event.data)); };
4. 代理服务器 (Proxy)
-
原理:让同源服务器代理转发请求(绕过浏览器限制)。
-
实现方式:
-
开发环境:Webpack DevServer / Vite Proxy
javascript// vite.config.js export default { server: { proxy: { '/api': { target: 'http://target-server.com', changeOrigin: true, rewrite: path => path.replace(/^/api/, '') } } } }
-
生产环境:Nginx反向代理
bashlocation /api/ { proxy_pass http://target-server.com/; proxy_set_header Host $host; }
-
5. postMessage
-
适用场景:跨窗口通信(如iframe与父页面)。
-
代码示例:
csharp// 发送方 iframe.contentWindow.postMessage('data', 'https://target.com'); // 接收方 window.addEventListener('message', event => { if (event.origin !== 'https://sender.com') return; console.log(event.data); });
6. 修改 document.domain (仅限子域)
-
限制 :仅适用于主域相同、子域不同的场景(如
a.example.com
↔b.example.com
)。 -
代码:
ini// 两个页面都设置 document.domain = 'example.com';
二、回答技巧
-
核心必答:CORS机制(尤其预检请求)、代理服务器原理。
-
对比方案:
- CORS vs JSONP:安全性、请求方法支持度、现代API兼容性。
- 代理服务器适用场景:解决开发环境跨域、隐藏真实接口地址。
-
安全提醒:
Access-Control-Allow-Origin: *
的风险- JSONP的XSS漏洞(需校验来源)
-
实际经验:提到Webpack/Vite代理配置或Nginx部署经验是加分项。
三、解决方案对比表
方案 | 适用场景 | 请求支持 | 安全性 | 复杂度 |
---|---|---|---|---|
CORS | 主流API交互 | 所有方法 | ★★★(需配置) | 中 |
JSONP | 老旧浏览器兼容 | GET | ★ (易XSS) | 低 |
代理服务器 | 开发环境/隐藏真实地址 | 所有方法 | ★★★ | 中 |
WebSocket | 实时通信 | 双向通信 | ★★★ | 高 |
postMessage | 跨窗口通信 | 数据传递 | ★★ | 中 |
💡 回答时强调"根据场景选方案":
- 现代项目首选CORS
- 本地开发用代理
- 特殊场景考虑WebSocket/postMessage
四、document.domain
跨域解决方案详解
document.domain
是一种专门用于解决主域相同、子域不同的跨域问题的技术。下面我将详细讲解其原理、使用方法和注意事项。
原理机制
document.domain
的工作原理基于以下关键点:
- 同源策略限制:浏览器默认阻止不同源(协议+域名+端口)页面间的交互
- 域名继承关系 :子域(如
a.example.com
)继承自主域(example.com
) - 属性设置 :通过将
document.domain
设置为相同的主域,浏览器会认为这些页面同源
使用场景
仅适用于以下情况:
- 主域名相同(如
example.com
) - 子域名不同(如
app1.example.com
和app2.example.com
) - 使用相同协议(都使用 HTTP 或 HTTPS)
- 使用相同端口(如都使用 80 端口)
具体使用步骤
1. 在两个页面中都设置 document.domain
在需要相互通信的两个页面(如主页面和 iframe 页面)中,都需要设置 document.domain
为相同的主域:
xml
<!-- 主页面:http://app1.example.com/index.html -->
<script>
// 设置为主域名
document.domain = 'example.com';
</script>
xml
<!-- iframe 页面:http://app2.example.com/widget.html -->
<script>
// 同样设置为主域名
document.domain = 'example.com';
</script>
2. 通过 iframe 实现跨域通信
父页面访问 iframe 内容
xml
<!-- 父页面:http://app1.example.com/index.html -->
<iframe id="childFrame" src="http://app2.example.com/widget.html"></iframe>
<script>
document.domain = 'example.com';
const iframe = document.getElementById('childFrame');
iframe.onload = function() {
// 现在可以安全访问 iframe 内容
const childWindow = iframe.contentWindow;
// 调用 iframe 中的函数
childWindow.childFunction();
// 访问 iframe 中的变量
console.log(childWindow.childVariable);
};
</script>
iframe 访问父页面内容
xml
<!-- iframe 页面:http://app2.example.com/widget.html -->
<script>
document.domain = 'example.com';
// 访问父页面的全局变量
console.log(parent.parentVariable);
// 调用父页面的函数
parent.parentFunction();
</script>
3. 使用 window.open() 打开窗口的跨域通信
ini
// 主页面:http://app1.example.com/index.html
document.domain = 'example.com';
const newWindow = window.open('http://app2.example.com/page.html');
// 等待新窗口加载完成
newWindow.onload = function() {
// 访问新窗口的内容
newWindow.childFunction();
};
xml
<!-- 新窗口:http://app2.example.com/page.html -->
<script>
document.domain = 'example.com';
// 访问打开它的窗口
opener.parentFunction();
</script>
关键注意事项
-
设置限制:
- 只能设置为当前域名的后缀(如
sub.example.com
只能设置为example.com
) - 不能设置为不相关的域名
- 不能设置为公共后缀(如
.com
,.org
)
- 只能设置为当前域名的后缀(如
-
端口重置:
- 设置
document.domain
后,端口号会被重置为null
- 两个页面必须都设置相同的
document.domain
- 设置
-
现代浏览器限制:
- Chrome 等现代浏览器要求设置的值必须是当前域名的有效父域
- 如果使用了 HSTS(HTTP Strict Transport Security),可能无法修改
document.domain
-
安全性考虑:
- 放宽了同源策略限制,需确保只与信任的页面通信
- 避免设置过于宽泛的域(如顶级域名)
要点总结
"document.domain
是一种解决主域相同、子域不同的跨域方案。它通过将两个页面的 document.domain
设置为相同的主域,使浏览器将它们视为同源。使用步骤是:1) 在两个页面中都设置 document.domain = '主域名'
;2) 通过 iframe 或 window.open 进行通信。但需要注意它只能用于子域之间,现代浏览器有一些限制,并且设置后会重置端口号。在实际项目中,更推荐使用 CORS 或 postMessage 等更安全的方案。"