切换主题
从问题出发
在讨论性能优化前先来补充一点前置知识,了解一下如何评估性能?
最简单的你可以使用
lighthouse
,当然除了lighthouse
之外,Web接口
专门提供了相应的API
供开发者使用,比如Performance API
,我们来看看其中的PerformanceNavigationTiming
中定义的所有时间戳属性:
有了前置知识,关于前端性能优化,我想从一个老生常谈的问题切入,什么问题呢?
从输入URL
到看见页面发生了什么?
比如,当我们输入在浏览器地址栏输入https://muri.life/
(或者直接点击此链接时)并按下Enter
键,到页面显示出内容会依次经过以下过程:
- 检查有无重定向
性能优化
- 尽量的避免重定向。
- 查找缓存
按照
浏览器缓存
=>系统缓存
=>路由器缓存
=>运营商DNS缓存
=>根域名服务器
=>顶级域名服务器
=>主域名服务器
的顺序逐步读取缓存。性能优化
DNS
缓存优化。为什么不是先存放在
浏览器缓存
中,而是缓存到系统缓存
中?浏览器缓存
是浏览器自动设置的,有效时间一般为1分钟
;系统缓存
是开发者设置的,有效时间一般为TTL
时长(默认为1小时
)。
DNS
解析若查找缓存失败,则开始解析
DNS
,获得对应域名的IP
地址。性能优化
DNS
预解析:提前解析之后可能会用到的域名,将解析结果缓存到系统缓存
中,缩短DNS
解析时间,进而提高网站的访问速度。如何配置
DNS
预解析?- 通过修改
link
标签rel
属性<link rel="dns-prefetch" href="https://muri.life/">
; - 指定
HTTP
标头Link: <https://muri.life/>; rel=dns-prefetch
。
- 通过修改
- 发起
TCP
连接经过三次握手建立
TCP
连接。性能优化
- 多花钱。
- 发送
HTTP
请求请求报文
由请求行
、请求报头
、请求正文
组成。性能优化
- 使用
CDN
加速; - 减少资源大小:
- 代码压缩;
- 图片压缩;
- 组件按需加载。
- 减少
HTTP
请求数:HTTP
强缓存;- 本地存储,像
localStorage
; - 合并请求,像
雪碧图
。
- 使用
- 服务器处理请求并返回
HTTP
报文HTTP报文
由状态码
、响应报头
、响应报文
组成。性能优化
- Redis缓存;
- 使用
Gzip
、Brotli
压缩资源。
- 浏览器解析渲染页面
- 解析
HTML
生成DOM树
,解析CSS
生成CSSOM树
;虽然
加载CSS
不会阻塞DOM树
的解析,但是会阻塞DOM
的渲染和后面JavaScript
语句的执行。性能优化
- 合并多个
CSS
文件,或者直接将首屏关键CSS
写成内联样式(内联样式不能被缓存) - 异步加载
CSS
JavaScript<link rel="stylesheet" href="mystyles.css" media="noexist" onload="this.media='all'"> // 或者 <link rel="alternate stylesheet" href="mystyles.css" onload="this.rel='stylesheet'">
@import
会影响浏览器的并行下载,不要使用@import
- 合并多个
- 合并
DOM树
和CSSOM树
生成Render树
;由于
Render树
依赖CSSOM树
,必须等待到CSSOM树
构建完成,即CSS
资源加载完成(或者CSS
资源加载失败)后,才能开始渲染,这也是为什么加载CSS
会阻塞DOM
的渲染。
以下两种情况JavaScript
的执行会阻塞DOM
和CSSOM
的构建:JavaScript
文件被放置在head
标签内部(暂停HTML
解析,先执行JavaScript
);JavaScript
代码修改了DOM
结构(回流和重绘)。
性能优化
- 使用defer或async
JavaScript<script src='xxx' async></script> // 异步加载脚本,在文档渲染完毕后,DOMContentLoaded事件调用前按顺序执行 <script src='xxx' defer></script> // 异步加载脚本,在允许的情况下,谁先加载完谁先执行
- 使用Web Worker。
- 根据
Render树
渲染并绘制页面。这里涉及了回流和重绘。
性能优化
- 尽量避免回流和重绘。
- 解析
- 连接结束
经过四次挥手断开连接。