《工欲善其事,必先利其器》
关于前端性能优化,是我们老生常谈的一个知识点了。性能优化的概念非常多,诸如 FP(First Paint)
、FCP(First Contentful Paint)
、LCP(Largest Contentful Paint)
、TTI(Time To Interactive)
和 TBT(Total Blocking Time)
等等。
但仅仅了解概念是不够的,实际工作上需要如何落地这些优化方案,对我们开发还是有着一定的难度,这也是前端进阶之路上必然需要经历的。否则你将永远无法晋升为中级/高级前端工程师。接下来我会就其中几点,发表一些自己的看法和优化方案。
我自认为我还是不成熟的,如果文中有地方描述的不正确的地方,我愿意接受大家的指正,也非常欢迎大家一起共同探讨更优的方案!
首屏渲染
优化常用的手段是使用加载画面,待首屏加载完成之后,取消加载画面。这个功能的实现我们可以通过 Performance API
来获取其加载的时间,从而控制加载画面的显隐。
虽然它有存在的必要,但却并不是长久之计,当首页的网络请求很多时,为了用户的体验着想,我们可不能让用户的界面一直停在那里转圈圈哦。
当首页需要加载多个网络请求
何为分片请求?例如首页总共有10个网络请求,我们没有必要等待10个网络请求全部加载完之后,再取消加载画面。
相反的,我们可以优先加载2个比较重要的网络请求,利用这2个网络请求,来控制加载画面。也就是说,相比于上面加载全部的网络请求,我只需要加载2个,这两个网络请求加载完成之后,取消加载画面,执行回调函数,继续请求其他网络请求。
实际上,这个知识点是懒加载。利用这一点,我们可以实现:组件懒加载和内容的懒加载。
而关于如何实现懒加载,网络上也是五花八门。我这里总结了几个比较靠谱的实现方式,大家可以一起来看一看:
el.offsetTop - document.documentElement.scrollTop - T <= el.clientHeight
。其中,变量 T
是前置高度,即如果你不想让视图滚动到区域顶部加载,而是提前多少像素高度加载;el.getBoundingClientRect()
可以获取这个元素的长宽距离顶部等参数,计算的方式与上一条是相同的;new IntersectionObserver()
交叉观察对象 API。如果两个对象交叉重叠了,就会执行回调函数。这种方式的好处就是可以不用监听滚动和元素对象,缺点是需要调整浏览器的兼容性。vue-lazyload
插件。这款插件帮我们做了兼容,可以选择是否使用 IntersectionObserver
。vue3 版本使用 vue-lazyload-next
。vueuse
插件库。同 IntersectionObserver
一样,vueuse 也封装了这个 API,我们可以直接只用 useIntersectionobserver
实现可视区域交叉的判断。或许你听说过 “圣杯布局
” ?还是听说过 “双飞翼布局
” ?如果听说过那么使用它的意义又是什么?
由于 JavaScript 是单线程自上往下执行的,那么考虑到数据太多的情况下,加载的顺序会受到影响,我们需要先让 js 引擎加载重要的内容,因此就产生了圣杯布局和双飞翼布局。
如上图,我们实现布局权重时,需要把中间栏的代码块,放在左右部分的代码块之前,就能稍微提升用户的使用体验。
所有 script 标签会严格按照他们在网页中出现的次序被解析。而使用 defer 属性可以把脚本推迟到文档渲染完毕之后再执行,相反 async 属性则是异步加载,两者作用不同。但对于单页面应用用处不大,因为单页面应用一般是通过 script 标签引入 JavaScript 来渲染 DOM 的。
列表渲染常用的优化方式是分片渲染,也就是分页。
属于数量不定的叠加网络请求
就像掘金网站采用的优化方案,分片渲染+图片懒加载+骨架屏。图片懒加载的作用是列表翻页之后,图片加载过慢时代替的方案;而骨架屏:一是为了用户进入网站时加载过慢的首屏优化(其实就是加载动画);二是防止用户滚动速度过快而产生的空白断层,从而提升用户体验。
注意,懒加载和虚拟列表是不同的概念和方案,不能混为一谈。前者更多的是防止由网络不稳定情况导致的用户体验问题,而后者更多的是防止 DOM 渲染过度的性能问题导致用户的体验问题,两者的侧重点不同。
在实际的工作中,列表项必然不会仅仅只由一个 li 标签组成,必然是由复杂 DOM 节点组成的。那么可以想象的是,当列表项数过多并且列表项结构复杂的时候,同时渲染时,会在 Recalculate Style 和 Layout 阶段消耗大量的时间。
而虚拟列表就是解决这种问题的一种方案,即只对可见区域进行渲染,对非可见区域中的数据不渲染或部分渲染的技术,从而达到极高的渲染性能。
如上图,假设有1万条记录需要同时渲染,我们屏幕的可见区域的高度为500px,而列表项的高度为50px,则此时我们在屏幕中最多只能看到10个列表项,那么在首次渲染的时候,我们只需加载10条即可。
考虑到成千上万条复杂的 DOM 节点形成的列表,必然会导致浏览器的重绘,消耗的性能无法估量。因此,当滚动发生时,虚拟列表将动态的通过计算获得可视区域内的列表项,并将非可视区域内存在的列表项删除。
骨架屏也是为了防止用户滚动过快而导致的页面空白断层,而防止这一种现象的产生可以使用模拟滚动条,控制用户的滚动速度,来间接的提升用户的体验。
属于网页的重要元素之一
如上图,当网页中图片加载过慢时,我们可以使用一张前置静默图片来代替图片的加载的空白期,从而提升用户体验(其实也就是我们上面提到的可视区域加载)。可以是网站的标志性图片,也可以是标志性动图,大部分是使用 loading 动画。
当然,当图片加载失败时,我们也可以使用 其他图片代替,这也能稍微提升用户体验。
用户体验要考虑的地方实在是太多啦,目前我学习到的仅仅是以上一些比较常用的知识点,后续会继续学习,继续分享,本篇文章到此就完结了,非常感谢你的阅读!
希望你的未来一片光明。