切换主题
从问题出发
在讨论性能优化前先来补充一点前置知识,了解一下如何评估性能?
最简单的你可以使用
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
属性
JavaScript<link rel="dns-prefetch" href="https://muri.life/">
- 指定
HTTP
标头Link
HTTPLink: <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// 异步加载脚本,在DOM解析完成后,DOMContentLoaded事件调用前按顺序执行 <script src='xxx' defer></script> // 异步加载脚本,谁先加载完谁先立即执行(可能阻塞DOM解析) <script src='xxx' async></script>
- 使用Web Worker。
根据
Render树
渲染并绘制页面。这里涉及了回流和重绘。
性能优化
- 尽量避免回流和重绘。
连接结束
经过四次挥手断开连接。