以性能优化为核心串联前端校招面试中HTTP/页面渲染99%知识点

328 阅读6分钟

✊不积跬步,无以至千里;不积小流,无以成江海

前置相关文章

前端性能优化的基本场景: juejin.cn/post/734582…

性能优化在项目中实战: juejin.cn/post/734132…

浏览器中的基础知识: juejin.cn/post/740326…

HTTP面试小结: juejin.cn/post/734426…

摘要 - 性能优化

【可测量指标】:DomReady(DomContentLoaded)事件发生时

【可使用工具】:Network、performance、rendering、FPS、coverage

【可优化方法】:DNS prefetch

TCP:连接复用、并行化、HTTP管道、多路复用、server push

HTTP:资源合并、内联、压缩、精简、cookie free、CDN、缓存、内容协商

代码:位置、拆分、动态导入、懒加载、预加载、CSS优化、JS优化

两个常考问题 - 基础篇

前端面试中经常遇见这两个常考的、非常大的、一旦被面试官深挖经常会哑口无言的问题:

  1. 输入URL发生了什么?
  2. 浏览器如何渲染页面

看似这两个问题是在考察你对整个浏览器原理的理解程度,但实际上不仅限于此,当你弄清这两个问题中所有的过程以及逻辑以及各个环节的关联,其实是在为前端的性能优化做准备。

了解清楚这些知识点,才知道前端能够性能优化的点在哪里,这也对以后工作中有帮助。话不多说,且看下文。

graph LR; A(用户在浏览器输入 URL)-->B(DNS 解析); B-->C(建立 TCP 连接); C-->D(发送 HTTP 请求); D-->E(服务器处理请求); E-->F(服务器返回 HTTP 响应); F-->G(浏览器解析响应); G-->H(浏览器渲染内容);
graph LR; A["浏览器接收 HTML、CSS、JS 等资源"] --> B["构建 DOM 树"]; B --> C["构建 CSSOM 树"]; C --> D["结合 DOM 和 CSSOM 生成渲染树"]; D --> E["布局(计算每个元素的位置和大小)"]; E --> F["绘制(将元素绘制到屏幕上)"];

上面两个流程图就是前面两个问题的答案。

截屏2024-09-28 15.53.40.png


  • DNS - domain name system - 给他一个域名(host),返回一个1.2.3.4,就是dns
  • 发送url后,检查缓存 - 先浏览器 - 再操作系统(会储存hosts文件) - 后ISP(电信移动等服务商)
  • 三次握手 - 同步信息回应再同步 - 第三次是为了防止错误情况,验证是否能接受
  • 四次挥手 - 同步回应确认再回应 - 四次回应是因为,存在回应和确认中有附加信息的情况
  • TCP - 三次握手 & 四次挥手的全过程就是一次tcp连接

  • HTTP请求 = 请求行 + 请求头 + 换行 + 消息体
  • HTTP响应 = 状态行 + 响应头 + 换行 + 消息体

  • HTML解析被JS阻塞 - 当遇到js的时候要优先下载并执行js后再返回,所以这会导致js阻塞html
  • async 与 defer - defer都执行完毕后才会执行domready - async在HTML解析完成后立刻执行domReady
  • JS被CSS阻塞 - 只有下载完CSS才能执行js
  • 布局+绘制+合成 - 拿到dom树(只有布局)和css树(只有样式)之后,合成一个渲染树(节点及样式),如下图
  • 重绘(repaint)和重排(reflow) - 页面更新时产生 - 重排必重绘;重绘可能只重新合成

截屏2024-09-28 17.59.05.png

性能优化 - 工具

打开开发者工具,找到检查即可查看运行情况

  • Network:观察瀑布流中的request sent/waiting(服务器or网络带宽)/下载文件时间;蓝线domready;红线资源被下载
  • performance - 看js的执行性能;如果一个函数在很低的执行层级(最下面表示嵌套多层),则自身执行时间太长
  • rendering - 能够看哪些地方进行了重新绘制
  • FPS - 滚动观察帧率
  • coverage - 查看代码使用率(红色未使用)

性能优化 - 方法

  1. DNS prefetch 预解析,一种方法是通过在dns中的head头加入dns- prefetch标签
<link rel="dns-prefetch" href="//example.com">

2. TCP

  • 连接复用:keep-alive - 之前是请求+响应🔁,即开tcp-请求-响应-关tcp-开tcp;有了它省去了先关再开,方法是把它放在请求头&响应头中,并可以设置关到开的间隔时间,是一种串行。【HTTP/1.1
  • 并行化:并发连接,在上面串行的基础上增加几个并行的tcp,大致是6-12个,经常和上面的连接复用组合出现,形成【HTTP/1.1】中的连接复用和并发连接。比如,将一个css拆成三个css。
  • HTTP管道:保持tcp一直开的状态,有新的请求就发出去并接响应回来。但缺点是必须依照发请求的顺序来给响应,如果某个特别大,后面的也只能等着。
  • 多路复用:【HTTP/2】基于帧实现(【HTTP/1.1】基于字符串实现);基本帧为9字节+最大16兆数据,前面的请求头/消息体在HTTP/2中会分成几帧发送;于是,再有多个请求发送,无需等待下载完成直接响应即可(因为那9个字节就相当于身份验证符)
  • server push:说白了就是主动push文件过去,需后端手动配置,即当收到某个请求的时候给响应的的同时把可能要请求的文件push过去。

3.HTTP(无法升级到HTTP/2;需要在HTTP/1.1层面进行优化)

  • 资源合并:CSS Sprit- 将四个方向的图合成一个;icon font- 将icon用标签指代;svgSymbols- 将svg id化,用标签显示icon,和2比好处是可以渐变
  • 内联:将小的文件转换成script标签中的内容;优点:可减少时间
  • 压缩:在响应发给浏览器之前压缩成一个gzip包;ngnix上写gzip on就可以,可以用type指定压缩类型
  • 精简:通过webpack插件 - HTML删闭合、删空格;CSS删未用;JS改过长变量名、tree shaking(移除未使用代码);SVG删除无用标签以及属性;减小图片体积
  • cookie free:减少cookie体积来增强性能 - 用localStorage/sessionStorage;启用新域名实现cookie free,将css/js等文件放到新的域名中
  • CDN:内容分发网络;通过DNS的负载均衡(将ip放到不同的地址,使每个地址的值返回同内容),在物理的距离上缩短时间;通过购买服务-命令行上传-改html的src;优点:cookie free、并行or多路复用、下载快;缺点:开销大、可控性差、跨域CORS
  • 缓存:(强缓存)CDM上会缓存你需要的除了html之外的文件,需要时调用;如果有了新修改的文件,会存在原文件依旧在cdn缓存上这种情况,因为大概率会发一个新的文件过去。【CacheControl】包含:公开/私有(表示除了用户和服务器终端其他都没有) + 缓存时间(max-age) + 必须重新校验 -> 重新校验即内容协商👇
  • 内容协商:(弱缓存)协商缓存过期后还能不能重用。【HTTP/1.1】:过期后要进行协商请求(请求头为ETag指纹+if-none-match)- 返回304代表没变;返回200附加新的文件。
    • HTTP/1.0】的缓存变为expires/lastmodify;内容协商变为if-modified-since

4.代码

  • 位置:css先加载,js后执行 - 有助于用户先看到页面。避免白屏or闪烁的现象发生(这个现象的发生就是因为css下载慢了)
  • 拆分:利用webpack将js/css代码拆分,比如分成runtime/vendor(第三方库)/page(当前页逻辑),根据代码变动频率进行拆分
  • 动态导入:将首页分成一个js/后面的分成另一个js,等后面需要再加载后面的js(React中的suspense&lazy)
  • 懒加载:将图片先用一个小图片替代,将真正source写到data-src里面
  • 预加载:避免懒加载太懒而产生的预加载 - 在滚到之前提前加载一部分
  • CSS优化:1.删除无用css 2.减少重排 3.不使用@import url.css(这样会串行)4.启用GPU加速:translate3d
  • JS优化:1.尽量不用全局变量 2.少操作dom 3.少插HTML 4.使用节流/防抖来降低重排频率