前言
性能优化是每个前端都绕不过去的课题。说到性能优化,网上一搜一大把《前端性能优化的N条建议》,诸如:
1.减少 HTTP 的请求数;2. 使用 CDN;3. 添加 Expires 头;4. 避免重定向;5. 将 CSS 样式放在页面的上方;6. 将脚本移动到底部;7. 减少 DOM 操作;8. 给图片设置尺寸;9. 减少 DNS 查询;10. 压缩 JavaScript 和 CSS……
噼里啪啦一大堆,这对于记性不大好的我来说,这种琐碎且毫无章法的1.2.3,太难了啊,能不能先归纳一下?比如说:
网络层面的性能优化:
1.减少 HTTP 的请求数;2. 使用 CDN;3. 添加 Expires 头;4. 避免重定向;9. 减少 DNS 查询;10. 压缩 JavaScript 和 CSS ……渲染层面的性能优化:
5. 将 CSS 样式放在页面的上方;6. 将脚本移动到底部;7. 减少 DOM 操作;8. 给图片设置尺寸;……
嗯,这样好像好点儿,但能不能更清晰一些,这些道理都是谁说的,为啥呀?哎,有没有一条可循的线索,把这些零零散散的东西串起来?直到我拜读《前端性能优化之关键路径渲染优化》、《前端性能优化原理与实践》,豁然开朗:只要循着网络请求的过程和浏览器渲染机制去探索,就知道为什么、以及怎么做了。
网络请求的角度
网络请求的过程
- DNS解析,获取 URL 对应的 IP 地址
- 根据 IP,建立 TCP 连接(三次握手)
- HTTP 发起请求
- 服务器处理请求,HTTP 响应返回(浏览器渲染页面,放在下一部分)
- 关闭 TCP 连接(四次挥手)
优化方向思考
- DNS 解析:可以使用浏览器 DNS 缓存和 DNS prefetch 减少解析次数或者把解析前置
- TCP 连接:长连接、预连接、接入 SPDY 协议
- HTTP 请求:减少请求次数和减小请求体积方面,具体有利用浏览器缓存、本地存储、资源的合并和压缩、图片优化等。
页面渲染的角度
渲染机制
浏览器渲染的基本步骤
- 处理 HTML 标记并构建 DOM 树
- 处理 CSS 标记并构建 CSSOM 树
- 将 DOM 与 CSSOM 合并成一个 render tree
- 根据渲染树来布局,以计算每个节点的几何信息
- 将各个节点绘制到屏幕上
值得注意的是,Render Tree 依赖于 CSSOM Tree,如果页面引用了外部样式表,页面需要等待样式表加载完毕才能进行渲染。另外,JS 引擎和 UI 的渲染引擎是互斥的,所以当脚本在执行的时候浏览器要将控制权就给 JS 引擎,等到 JS 执行完毕再还给 UI 引擎(这与浏览器的进程管理有关)。
回流与重绘
以上只是一个理论的渲染过程,但实际的页面渲染并不是那么一帆风顺从上往下渲染就完了的。比如说,如果在渲染过程中碰到的一个同步的 Script ,JS 改变了 DOM 结构,会发生什么?这里需要提到“回流与重绘”的概念。
优化方向思考
- 对于页面首次渲染,减少阻塞因素
- 尽量避免导致回流与重绘的操作
- 利用懒加载实现首屏渲染优化
- 服务端渲染 …
有了以上对网络请求的流程 和 页面渲染机制的认识,现在可以理解为什么要把CSS文件放在页面最上方、JS文件放在最下方、为什么要减少DOM操作等等。
实际上还有很多细节的地方没弄清楚,这里咱先提个方向,更多的具体方案在后面的篇章再展开看看。
参考链接
前端性能优化之关键路径渲染优化
从 8 道面试题看浏览器渲染过程与性能优化
前端性能优化原理与实践
史上最全!图解浏览器的工作原理