1、跨域问题解决的方案以及引起跨域的问题 ?
答:跨域是指浏览器出于安全策略,阻止一个网站的JS脚本去请求另一个源(协议 + 域名 + 端口)的资源。这是浏览器的同源策略所致。
常见的解决方式:
- CORS(服务端支持):后端配置响应头允许它跨域请求,由服务器“授权”跨域访问。
res.setHeader('Access-Control-Allow-Origin', '*'); // 或具体域名
res.setHeader('Access-Control-Allow-Methods', 'GET,POST,PUT,DELETE');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
- JSONP(仅支持 GET,已过时),原理是
<script>不受同源策略限制。服务端返回callback({data})格式。
<script src="http://api.example.com/data?callback=handleData"></script>
function handleData(res) {
console.log(res);
}
- 代理转发(开发环境常用)
- 本质上是 同源请求 → 代理服务 → 请求目标服务。
- 常见于前端开发时绕过 CORS 限制。
// vite.config.ts
server: {
proxy: {
'/api': {
target: 'http://api.example.com',
changeOrigin: true,
rewrite: path => path.replace(/^\/api/, '')
}
}
}
// 开发中访问 /api/user,实际上由代理服务器转发到 http://api.example.com/user,浏览器不感知跨域。
- Nginx 反向代理,在生产中常用 Nginx 配置跨域代理:
location /api/ {
proxy_pass http://api.example.com/;
add_header Access-Control-Allow-Origin *;
}
- WebSocket 不受同源限制;WebSocket 是一种不同协议(ws://),不受 CORS 限制,可以作为某些实时跨域通信的替代。
- iframe + postMessage(跨域通信);用于多个子应用间或主子应用通信:
// 父窗口向 iframe 发消息
iframe.contentWindow.postMessage('hello', 'http://target.com');
// 子窗口接收
window.addEventListener('message', e => {
if (e.origin === 'http://your-allowed-origin.com') {
console.log(e.data);
}
});
为什么表单提交不会出现跨域
- 因为表单提交是非常老的机制,早在浏览器发明之前就有,浏览器内部控制发送和接收
- 没有开放给 JS 控制数据
- 不会泄露用户信息到 JS 层
所以浏览器认为它是 “低风险”跨域,就允许了。
2、解释浏览器的事件循环(event loop)机制以及它与前端的异步编程有何关系?
- 浏览器
Event Loop是一个协调“执行同步任务”和“处理异步回调”的机制,它不断循环检查任务队列,把异步任务的回调推入主线程执行。 - 当JS执行遇到
setTimeout,Promise,async/await等异步代码时,浏览器会将这些异步任务注册到Web APIs区域,等主线程空闲后再通过事件循环(Event Loop)将回调推入任务队列执行。事件循环的存在使得JavaScript 单线程模型也能实现异步非阻塞编程。
3、宏任务微任务
答:宏任务和微任务是JS在事件循环中安排异步任务执行的两个队列(任务类别)。在一次事件循环中,先清空微任务队列,再执行一个宏任务。
- 常见的宏任务:
setTimeout,setInterval,setImmediate,I/O,MessageChannel,requestAnimationFrame(部分实现),当前宏任务执行完成后,下一轮 Event Loop 开始前 - 常见的微任务:
Promise.then()、catch()、finally()、MutationObserver、queueMicrotask()当前宏任务执行后、下一轮宏任务开始前
注:
Promise.then比setTimeout先执行(因为微任务优先)async/await中await后面的代码其实是微任务- 浏览器绘制帧(如动画)通常在宏任务之后触发
浏览器事件循环简化版流程(核心阶段)
1、执行宏任务(Task)队列中的一个宏任务
- 宏任务包括:事件回调(点击、键盘等)、定时器回调(setTimeout)、网络回调、脚本执行等。
- 执行过程中同步代码直接运行,且产生的异步操作(Promise、queueMicrotask)排入微任务队列。
2、执行当前宏任务产生的所有微任务(Microtasks)
- 包括
Promise 回调、queueMicrotask等。 - 这些微任务会在当前宏任务结束后立即同步执行,直到微任务队列清空。
3、执行所有排队的 requestAnimationFrame 回调(Animation Frame Callbacks)
- 这一步发生在当前宏任务和微任务执行完后,渲染之前。
- 这些回调会在绘制前执行,方便同步动画更新。
4、浏览器绘制(渲染)阶段
- 根据 DOM/CSS 的变化,浏览器执行布局(reflow)、绘制(repaint)等渲染操作,将更新后的画面显示到屏幕上。
5、然后开始下一次事件循环,继续执行下一个宏任务。