前言
基于现代Web
前端框架的应用,其原理是通过浏览器向服务器发送网络请求,获取必要的index.html
和打包好的JS、CSS
等资源,在浏览器内执行JS
,动态获取数据并渲染页面,从而将结果呈现给用户。在这个过程中,有两个步骤可能较为耗时,一个是网络资源的加载,另一个是浏览器内代码执行和DOM渲染。而耗时的增加会导致页面响应慢,卡顿,影响用户体验,针对上述两种耗时的情况,常见的优化方向有:
- 缩短请求耗时;
- 减少重排重绘;
- 改善JS性能。
下面讲一下如何减少重排重绘:
减少重排重绘
除了网络资源以外,另一个影响前端性能的因素就是前端页面的渲染绘制效率。
虽然不同的前端框架有一些差异,但整体的优化思路是一致的比如下面的优化方案:
将 CSS 放在页面顶部,js放到底部
我们研究雅虎网页性能时发现把样式表移到 里会让页面更快。这是因为把样式表移到 里允许页面逐步渲染。
关注性能的前端工程师希望页面被逐步渲染,这时因为,我们希望浏览器尽早渲染获取到的任何内容。这对大页面和网速慢的用户很重要。给用户视觉反馈,比如进度条的重要性已经被大量研究和记录。在我们的情况中,HTML 页面就是进度条。当浏览器逐步加载页面头部,导航条,logo 等等,这些都是给等待页面的用户的视觉反馈。这优化了整体用户体验。
把样式表放在文档底部的问题是它阻止了许多浏览器的逐步渲染,包括 IE。这些浏览器阻止渲染来避免在样式更改时需要重绘页面元素。所以用户会卡在白屏。
脚本会阻塞并行下载,HTTP/1.1 官方文档建议浏览器每个主机名下并行下载的组件数不要超过两个,如果图片来自多个域名,并行下载的数量就可以超过两个。如果脚本正在下载,浏览器就不开始任何其它下载任务,即使是在不同域名下的。
有时候,并不容易把脚本移动到底部。举个例子,如果脚本是用 document.write 插入到页面内容中的,就没办法再往下移了。还可能存在作用域问题,在多数情况下,这些问题都是可以解决的。
一个常见的建议是用推迟(deferred)脚本,有 DEFER 属性的脚本意味着不能含有 document.write,并且提示浏览器告诉他们可以继续渲染。不幸的是,Firefox 不支持 DEFER 属性。在 IE 中,脚本可能被推迟,但不尽如人意。如果脚本可以推迟,我们就可以把它放到页面底部,页面就可以更快地载入。
避免使用 CSS 表达式
CSS 表达式是强大的(可能也是危险的)设置动态 CSS 属性的方法。IE5 开始支持,IE8 开始不赞成使用。例如,背景颜色可以设置成每小时轮换:
background-color: expression( (new Date()).getHours()%2 ? "#B8D4FF" : "#F08A00" );
表达式的问题在于它们的评估频率高于大多数人的预期。它们不仅在页面呈现和调整大小时进行重新计算,而且在页面滚动时甚至在用户将鼠标移动到页面上时进行计算。在 CSS 表达式中添加计数器可以让我们跟踪 CSS 表达式的计算时间和频率。在页面上移动鼠标可以轻松计算超过 10,000 次。
使用外部 JavaScript 和 CSS
在现实环境中使用外部文件通常会产生较快的页面,因为 JavaScript 和 CSS 有机会被浏览器缓存起来。对于内联的情况,由于 HTML 文档通常不会被配置为可以进行缓存的,所以每次请求 HTML 文档都要下载 JavaScript 和 CSS。所以,如果 JavaScript 和 CSS 在外部文件中,浏览器可以缓存它们,HTML 文档的大小会被减少而不必增加 HTTP 请求数量。
决定是否使用外部文件的关键在于被缓存的外部文件占请求的 HTML 文档数的比重。如果网站用户在每次会话中进行多次页面访问,同时页面重用了多个脚本和样式表,使用外部文件时很好的选择。
对于大多数网站而言,难以精确度量以判断是否使用内联或外部文件,此时建议是使用外部文件的方式。对于这个问题的一个例外是网站主页,由于主页对于响应时间要求更高,因此更加倾向于内联而不是外部文件。
对于内联文件而言,由于无法利用浏览器缓存,因此给人感觉依然比较低效。我们可以通过加载后下载和动态内联的方式来使得网站主页既可以获得内联的优势,同时也能缓存外部文件。
减少渲染量
总体原则:不渲染未展示的部分
常用工具:
react-window
react-loadable
JS
原生,如IntersectionObserver
- 框架提供,如
React.lazy、react-intersection-observer
常用方法:
- 虚拟列表:只渲染可见区;
- 惰性加载:无限滚动;
- 按需加载:页面只在切换过去时才加载。
减少 DOM 元素数量
从以下几个角度考虑移除不必要的标记:
是否还在使用表格布局? 塞进去更多的
仅为了处理布局问题?也许有更好、更语义化的标记。 能通过伪元素实现的功能,就没必要添加额外元素,如清除浮动。 浏览器控制台中输入以下代码可以计算出页面中有多少 DOM 元素:
为什么不使用表格布局?
- 更多的标签,增加文件大小;
- 不易维护,无法适应响应式设计;
- 性能考量,默认的表格布局算法会产生大量重绘
减少渲染次数
总体思路:避免重复的渲染。
常用工具:
lodash
JS
或框架自带
常用方法:
- 防抖与节流;
- 对于React函数组件来说,合理使用副作用,拆分无关联的副作用;
- 对于React类组件来说,可以使用
shouldComponentUpdate
或使用PureComponent
来优化渲染; - 利用缓存,如
React.memo
; - 使用
requestAnimationFrame
替代setInterval
执行动画。
最后
为大家准备了一个前端资料包。包含54本,2.57G的前端相关电子书,《前端面试宝典(附答案和解析)》,难点、重点知识视频教程(全套)。
有需要的小伙伴,可以点击下方卡片领取,无偿分享