网络
Fetch 与 XHR、Axios 的核心区别
总结:Fetch 是浏览器原生 Promise API,轻量但功能最少;XHR 历史包袱大,已不推荐新项目;Axios 在浏览器和 Node 都能用,内置拦截器、超时、取消、自动 JSON 处理
语法上: Fetch、Axios 基于 Promise API, 而 XHR 基于事件驱动
行为差异:
- Fetch 默认 同源请求才带 cookie, 而且 XHR 和 Axios 总是携带 cookie
- Fetch 和 XHR 需要手动序列化反序列化,而 Axios 能自动处理
- Fetch 和 XHR 没有响应/拦截器 API 和 Axios 进行了提供
保持前后端实时通信的方法
- 轮询: 客户端设置定时器,每隔几秒就向服务端发送请求,通过频繁地请求到达实时的效果。这种方式要求服务器的响应速度很快。优点:实现简单。缺点:轮询间隔长会导致用户不能及时拿到最新信息,体验变差;HTTP 连接变密集,资源浪费
- 长轮询:客户端发送 HTTP 给服务器之后,如果没有新消息,就一直等待。有新消息,才会返回给客户端。优点:做了优化,有较好的时效性。缺点是保持连接会消耗资源; 服务器没有返回有效数据,前端容易请求超时。
- iframe 流:在客户端中隐藏一个 iframe 元素,利用其 src 属性在服务器和客户端之间创建一条长连接,服务器向 iframe 传输数据(通常是 HTML,内有负责插入信息的 javascript),来实时更新页面。优点是消息能够实时到达;浏览器兼容好。缺点是服务器维护一个长连接会增加开销;
- websocket:WebSocket 基于 TCP 的持久化连接,是一种全双工通信,一旦 WebSocket 连接建立后,后续数据都以帧序列的形式传输,此时客户端服务端都可以进行数据的实时收发。
- SSE(Server-Sent Event) 是一种浏览器原生的单向、文本流数据推送技术,适合服务端实时文本推送,比 WebSocket 简单,比轮询省资源
浏览器输入 URL 发生了什么?
- URL 处理:(纠错/补全) 浏览器会检查输入的 url 是否为一个合法 url,若没有输入协议则默认补全 HTTP,若输入了非 ASCII 码字符(如中文),则转为 ASCII 码。
- 对此 url 入口进行缓存检查 如果发现本地已经有一条针对该 URL 的、尚未过期的缓存,直接使用缓存,若未命中,则进入 DNS 解析
- DNS 解析:
1.浏览器检查自身缓存中是否有此域名 IP 地址
2.若没有,检查操作系统的 hosts 文件是否有此域名缓存
3.如果还没有,则向本地的 DNS 服务器发送 DNS 查询请求。
4.本地 DNS 服务器如果没有缓存,则进行迭代查询:从根域名服务器开始,依次查询顶级域名服务器(如.com)、权威域名服务器,直到获取到 IP 地址。 - 建立 TCP 根据 IP 地址建立 TCP 连接,进行三次握手,如果启用 HTTPS 协议,还要进行 TLS 握手
- 发送 HTTP 请求拉取资源
HTTP 请求包括 请求行 + 请求头 + 空行 + 可选请求体
- 请求行:
Method: GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS, TRACE, CONNECT
Request-URI:绝对路径 + 查询串
HTTP 版本 HTTP/1.1(主流)或 HTTP/2(h2 协商后)或 HTTP/3(QUIC)
- 请求头:
| 类别 | 典型字段 | 作用 | |-----------------|---------------------------------------------|-----------------------------| | 主机 & 端口 |Host: www.example.com| 虚拟主机识别 | | 客户端信息 |User-Agent: Mozilla/5.0 …| 浏览器/系统指纹 | | 接受类型 |Accept: text/html,application/xhtml+xml;q=0.9,application/json;q=0.8| 内容协商 | | 编码 & 语言 |Accept-Encoding: gzip, deflate, br| 压缩算法、语言偏好 | | 缓存 |Cache-Control: no-cache| 协商/强缓存 | | Cookie |Cookie: uid=42; session=xyz| 状态保持 | | 内容类型 |Content-Type: application/json| 说明请求体格式 | | 内容长度 |Content-Length: 48| 请求体字节数 | | 来源 & 安全 |Referer: https://google.com| CSRF/CORS 判断 | | 认证 |Authorization: Bearer <jwt>| 令牌/基本认证 |- 空行:\r\n 结束头
- 请求体: 根据Content-Type识别请求体,主要有几种类别,JSON、表单URL编码、多文件、纯文本
- 空行:\r\n 结束头
- 请求头:
- 请求行:
- 服务器返回所需资源(html/js/css/其他) 获取所需资源,进行四次挥手,关闭tcp连接
- 渲染
总体步骤: 构建DOM树 -> 计算样式 -> 布局 -> 分层 -> 绘制 -> 分块 -> 光栅化 -> 合成- 构建DOM树:
HTML解析器(分词器/树构建器): 网络进程会从目标服务器拉取对应的HTML二进制文件,把获取到的二进制数据源源不断的交给渲染进程,渲染进程交付给HTML解析器。二进制字节码被解码成字符流后送入分词器,分词器将元素的开始标签和结束标签分开,字符流被转换为TOKEN,TOKEN被交付给树构建器来构建DOM。(整个过程是边下载边解析、边生成 TOKEN 边建 DOM 树。过程可以简化成一条流水线,属于流式构建)- 树构建器中的元素栈
元素栈决定了下一个 TOKEN 应该插入到 DOM 树的哪个父节点下,当解析器碰到某个结束标签 TOKEN 时,会到栈里从栈顶向下找同名元素;找到后把该元素以及它上面的所有元素全部弹出,表示它们都已经正常闭合。如果没找到对应元素,就触发 HTML 规范定义的错误恢复流程(自动补标签、忽略多余结束标签等)。 - 渲染阻塞
- 当HTML解析器解析到
<script>元素时 渲染进程给网络进程发送请求,从服务器拉取对应的js文件。HTML解析都会暂停执行直到js被下载完毕执行,此时会触发阻塞渲染 - 遇到CSS文件加载,
<link rel="stylesheet">不会阻塞后续 HTML 解析,但会阻塞脚本执行和首次渲染(RenderBlocking)。
- 当HTML解析器解析到
- 树构建器中的元素栈
- 样式计算: 把所有 CSS 规则按优先级、继承和默认值算成像素级最终值,给每个节点生成一张只读快照(ComputedStyle),供 Layout 阶段使用(详细展开太多)
- 布局(Layout / Reflow): 根据上一步得到的
ComputedStyle和 DOM 树结构,为每一个可见元素计算在视口中的确切几何信息(x, y, width, height、line-box、flex-item、grid-area),生成一棵布局树。首次全量布局叫 Layout,后续增量修改叫 Reflow。
• 过滤掉 display:none、不在视口的 content-visibility:auto 等不可见节点。
• 为伪元素(::before/::after)、和伪对象创建额外布局对象。
• 为每一个布局对象走一遍盒模型算法
• 根据display调用不同的布局算法
• 文字布局:根据 white-space、word-break、hyphens 断句、断词、断行 - 对布局树进行分层,再将图层分成图块,紧接着光栅化将矢量图块转换成像素图,最后合成绘制生成页面。
- 构建DOM树:
什么是重绘、重排,如何避免重排
重排(Reflow):当 DOM 变化影响元素的几何属性(如位置、尺寸),浏览器需要重新计算布局树,将元素重新放置到页面的正确位置,这个过程称为重排。重排会重新计算布局树并触发重绘,代价较高。
重绘(Repaint):当元素的外观变化但不影响布局时,浏览器只需重新绘制元素外观。重绘避开了布局树的重建和分层,代价相对较低。重排必定导致重绘,但重绘不一定会触发重排。
触发重排的常见操作:
- 页面初次渲染。
- 添加/删除 DOM 元素。
- 改变元素的尺寸或位置(如宽度、高度、边距)。
- 改变元素内容(如文字、图片大小)。
- 修改字体大小或浏览器窗口尺寸。
- 调用
offsetWidth、offsetHeight等属性。
优化性能的方式:
- 样式集中修改:避免逐一修改样式,减少重排次数。
- 脱离文档流:使用
absolute或fixed布局,使元素脱离文档流,避免影响其他元素的布局。 - GPU 加速:使用
transform、opacity等属性启用 GPU 加速,避免触发重排。