第5天:浏览器原理(二)与 手写 AJAX 封装
·
今天我们把前两天复习的浏览器原理(跨域、安全、进程模型、渲染优化)和手写题结合起来,深入探讨 网络请求 这个核心话题,并手写一个基于 Promise 的 AJAX 封装,从简单到进阶,彻底搞懂前端如何与服务端通信。
Q4:什么是跨域?如何解决跨域问题?
我的回答:
- 同源策略:协议、域名、端口完全一致才允许访问,否则跨域。
- 解决方式:
- 后端设置 Access-Control-Allow-Origin,可以是 * 或指定域名。
- 前端设置代理,如请求 /api 由代理转发到目标接口。
补充与深化:
1. 同源策略的细节
- 浏览器出于安全考虑,限制跨域请求,但某些标签不受限:<script src>、<img>、<link>、<iframe> 等可以加载跨域资源,但不能读取响应内容。
- 跨域限制主要是阻止恶意网站读取另一个网站的敏感数据,但允许发送请求(如表单提交)只是无法读取响应。
2. CORS(跨域资源共享)
后端设置头部是 CORS 的核心,但还有一些细节:
- 简单请求(无预检,直接发):满足特定条件(如 GET/POST、Content-Type 为 application/x-www-form-urlencoded 等),浏览器直接发送请求,通过 Origin 头告知来源,服务器返回 Access-Control-Allow-Origin。
- 预检请求:对于复杂请求(如自定义头、PUT/DELETE),浏览器会先发 OPTIONS 请求,服务器返回允许的 Access-Control-Allow-Methods、Access-Control-Allow-Headers 等,确认后再发真实请求。
- 还可以设置 Access-Control-Allow-Credentials 允许携带 Cookie,此时 Access-Control-Allow-Origin 不能为 *,必须指定具体域名。
3. 代理方案
“前端设置代理”是开发环境常用方案:
- Webpack DevServer 配置 proxy,将 /api 请求转发到目标服务器,绕过浏览器的同源策略。
- 生产环境常用 Nginx 反向代理,统一入口,避免跨域。
- 本质是让浏览器请求同源地址,由服务器代理转发。
Q5:常见的 web 前端攻击有哪些?如何防范?
我的回答:
- XSS:注入脚本,获取用户信息或监控操作,通过验证输入解决。
- CSRF:注入请求引导用户点击,通过同源策略、加强验证、双 token 解决。
补充和优化:
1. XSS(跨站脚本攻击)
- 原理:攻击者在目标网站注入恶意脚本,当用户访问时执行,窃取 Cookie、会话令牌或进行其他操作。
- 防范:
- 输入过滤:对用户输入进行严格过滤(如使用白名单)。
- 输出转义:在 HTML、JS、CSS 上下文中对特殊字符转义(如 <、>)。
- 使用 CSP(内容安全策略):限制页面可以加载的资源来源,即使注入脚本也无法执行。
- Cookie 设置 HttpOnly:防止脚本读取 Cookie。
2. CSRF(跨站请求伪造)
- 原理:攻击者诱导用户访问恶意网站,该网站利用用户的登录状态,向目标网站发起伪造请求(如修改密码、转账)。
- 防范:
- 同源检查:验证请求的 Referer 或 Origin 是否合法,但可能被伪造。
- CSRF Token:服务器生成随机 token 存在 session,表单提交时携带,服务器验证。
- SameSite Cookie:设置 Cookie 的 SameSite 属性为 Strict 或 Lax,限制跨站请求携带 Cookie。
- 验证码:敏感操作强制要求用户输入验证码。
- 二次验证:如支付密码。
Q6:浏览器的进程/线程模型是怎样的?
我的回答:
- 主进程:解析 HTML、预解析 CSS/JS。
- GPU 进程:操作光栅化、像素。
补充与深化:
- 主进程(Browser Process):负责浏览器界面(地址栏、书签)、网络请求、文件访问、跨进程通信等,不负责页面渲染。
- 渲染进程(Renderer Process):每个标签页一个独立进程(部分站点可能共享),负责页面渲染、JS 执行、事件处理等。内部包含多个线程:
- GUI 渲染线程:解析 HTML/CSS、构建 DOM/CSSOM、布局、绘制。
- JS 引擎线程:执行 JS 代码,与 GUI 线程互斥(JS 执行时会阻塞渲染)。
-
事件触发线程:管理事件队列,当事件触发时回调 JS 引擎。
-
定时器线程:处理 setTimeout、setInterval,计时结束后将回调放入事件队列。
-
异步 HTTP 请求线程:处理 AJAX 请求,请求完成后将回调放入事件队列。
- GPU 进程:处理 GPU 任务,如 3D 绘制、光栅化、合成图层,提升渲染性能。
- 网络进程:负责网络资源加载。
- 插件进程:每个插件一个进程,保证安全。
为什么是多进程
- 安全隔离:每个标签页独立进程,一个崩溃不影响其他。
- 性能利用:多进程可充分利用多核 CPU。
- 保护隐私:不同站点隔离。
Q7:你对浏览器渲染层优化的理解?
我的回答:
- 通过 will-change 进行分层,以及 transform 代替 margin 等。
补充与深化:
1. 为什么分层?
- 页面某些元素变化时,如果未分层,可能触发整个页面重绘。分层后,只重绘变化层,然后合成,提升性能。
- 浏览器会自动将某些元素提升为合成层(如 video、canvas、transform 动画等),也可以通过 will-change 提示浏览器提前优化。
2. 如何创建合成层?
- transform: translateZ(0) 或 transform: translate3d(0,0,0) 强制使用硬件加速。
- opacity 动画也会产生合成层。
- will-change: transform, opacity 等属性提前告知浏览器可能变化,但不要滥用。
3. 优化原则
- 减少重排和重绘:用 transform 替代 top/left(因为 transform 在合成层处理,不触发布局和绘制)。
- 避免强制同步布局:如先读 offsetHeight 再写样式,会导致浏览器立即计算布局,应分离读写。
- 使用 requestAnimationFrame 做动画:保证在每一帧开始前更新,避免丢帧。
- 减少图层数量:过多的图层会增加内存和合成开销。
手写题:用 Promise 封装 AJAX
今天的手写题是将理论付诸实践——封装一个基于 Promise 的 AJAX 函数,既能复习网络请求,又能巩固 Promise 用法。
我的代码(原生 XHR 版)
// 原生ajax版本
function ajax({ url, method = "GET", data }) {
return new Promise((resolve, reject) => {
let xhr = new XMLHttpRequest();
xhr.open(method, url, true);
xhr.send(data);
// 监听
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
try {
const result = JSON.parse(xhr.responseText);
resolve(result);
} catch (e) {
resolve(xhr.responseText);
}
} else if (xhr.readyState === 4 && xhr.status !== 200) {
reject("error");
}
};
});
}
结合 jQuery 的封装(简化版)
// 结合 jQuery 的封装(简化版)
function ajax({ url, type = "get", data }) {
return new Promise((resolve, reject) => {
$.ajax({
url: url,
type: type,
data: data,
success(res) {
resolve(res);
},
error(err) {
reject(err);
},
});
});
}
1. 为什么需要封装ajax?
在传统的JavaScript中,发送网络请求主要依靠 XMLHttpRequest 或 fetch。但XMLHttpRequest 基于回调,容易形成“回调地狱”,代码难以维护。Promise 的出现让异步操作变得更加优雅,通过 then 和 catch 可以实现链式调用,避免层层嵌套。
2. 进阶扩展
a. 超时控制
function myAjaxWithTimeout(url, timeout = 5000) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.timeout = timeout;
xhr.ontimeout = () => reject(new Error('Request timeout'));
// ... 其余代码
});
}
b. 取消请求
const controller = new AbortController();
const signal = controller.signal;
function myAjax(url, { signal }) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
signal.addEventListener('abort', () => {
xhr.abort();
reject(new Error('Request aborted'));
});
// ... 其余代码
});
}
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)