切换主题
从问题出发
在讨论性能优化前先来补充一点前置知识,了解一下如何评估性能?
最简单的你可以使用
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树渲染并绘制页面。这里涉及了回流和重绘。
性能优化
- 尽量避免回流和重绘。
连接结束
经过四次挥手断开连接。