前端性能优化

188 阅读7分钟

✊不积跬步,无以至千里;不积小流,无以成江海

总述

前端性能优化,一个老生常谈的问题。因为前端主要就是在解决页面的问题,那么排除掉页面本身实现方面的问题,剩下一个最大的问题就是如何优化页面展示的效率,即前端的性能优化问题。

性能瓶颈主要出现在三个场景:

  • 在开发时每次修改代码打包需要几分钟,太慢
  • 打开网站,等了几十秒才看到页面,太慢
  • 页面展现后,页面上动画不流畅。滚动页面或者拖拽元素卡顿感严重,甚至页面会崩溃

大体上讲,解决它们分为几个大类:

  1. 能否在打包过程中尽量缩短打包的时间,即在开发建构阶段优化性能
  2. 能否在下载这个网页资源的时候尽可能快,即在资源加载阶段优化性能
  3. 能否在展现页面的时候不卡顿,即在页面渲染阶段优化性能
  4. 能否在用户进行滑动、拖动等操作时不会影响用户体验,即在操作体验阶段优化性能

开发建构阶段

首先可以使用一些打包工具来辅助进行打包,比如webpack。

其次,可以在打包时尝试并发构建,即把单线程打包转换为多线程打包,让几个cpu同时工作。

然后,我们可以尝试去过滤不相关的目录,即在loader处理时如需要查找对应目录的对应文件,假设目录中文件过多,可以尝试过滤一些不必要被查找的内容,从而减少查找项。

最后,我们可以利用缓存的方式,如果两次请求一致,那么无需重新加载,直接使用缓存的东西即可。

资源加载阶段

资源加载时,能够提升速度的话,可以主要从几个方面来优化,一个是让资源本身尽可能小,一个是让资源数量尽可能少,一个是让资源间距离尽可能近,还有一个就是让传输的工具尽可能先进。

对于资源小来说,首先可以运用工具来进行资源的压缩,比如利用webpack的tree shaking,让没用到的库不打包,来尽可能缩小资源的体积。还可以在服务端开启Gzip,在传输数据的时候再压缩一次,从而在过程中又减小了体积。除了这些工具的层面,具体到细节,HTML/CSS/JS都是可以进行压缩合并的。然后,还有一个很重要的地方就是图片的压缩,图片本身是可以进行压缩的,之后对于svg/svg的icon还可以用sprite来进行压缩。除了上面提到的,用工具、以及对于资源本身进行的操作,还可以使用技巧来减少资源的渲染,比如说懒加载,即通过动态加载的方式,将资源分块化渲染,而不是一次性全部渲染完成,来减小资源。以及对公共的模块进行封装,避免代码的重复写入影响体积。

对于数量少来说,可以尽量减少文件的个数,即资源的合并,让资源发出的时候就尽可能少。以及可以对HTTP进行缓存,即重复的请求可能不会发出去,从而减少频繁的操作。

对于距离近来说,可以考虑减少传输间的距离,就像物流离发货近的地方收货也快,即使用cdn来让静态资源放在离用户最近的服务器上,从而减小加载速度。

对于改进传输工具,可以使用http2来传输数据。HTTP1是‘请求-响应-请求-响应’分段式的处理方法,但http2是‘请求-响应‘-’请求-响应’-’请求-响应’的并行式处理方式,叫做多路复用。http1传输的数据都是字符串/文本,但http2将它们分解成一个个很小的包,先分解到达后再组装,叫做二进制分帧。其次,之前的http1,发送请求时其实请求头都是一致的,这一点在http2中得到了改进,叫头部压缩。在http1中,浏览器发请求到服务器请求html,服务器会把html返回给浏览器,浏览器对请求进行解析,了解到请求中有script/url等等,浏览器再向服务器发送请求,服务器再给出相应的指令(比如说script)以此类推。但在http2中,第一次发送请求到服务器后,服务器会优先进行解析,如发现需要script/url等等,即在返回时一次性把所需内容给出去,叫服务器推送

页面渲染阶段

首先,通用技巧,把css放到head里面,把js放到底部。这样是为了尽可能避免内容闪烁或白屏等等。因为js加载是立即执行的,如果一上来先执行js是需要时间的,但用户看到页面的一瞬间其实不需要执行任何操作,所以先让用户看到页面,再去渲染对应模块会产生的操作是一个好选择。

其次,不重要的资源可以用async或defer进行异步加载。给script上加async或defer避免不重要的资源和重要的资源“抢流量”。

操作体验阶段

对应不流畅的场景,给出相应的优化方案,是优化操作体验的最好方式。那么不流畅的场景有:1.动画显示不连贯 2.页面滑动不流畅,加载一顿一顿的 3.执行某操作后没有给出相应的反馈 4.进行大消耗操作时性能消耗过大导致资源不够用(风扇呼呼响)

对于动画不流畅,首先能使用css3的animation尽量使用这个,而不是考虑用js加动画。假设场景:当给某个模块加动画时,鼠标点击动画呈现,鼠标点击其他模块的时候动画消失;则可以选择用class来加这个动画,尽管鼠标点击这个操作是由js控制的,但是动画这个class是由css3控制的,而css3消耗gpu,js消耗cpu,分开执行能够避免线程阻塞。然后尽量使用transform,而避免使用绝对定位left/top这种,原因同上,tranform会使用gpu渲染。最后在js动画中,尽量使用requestAnimationFrame,而不是使用setInterval。

对于滚动/滑动页面不流畅。首先页面的滚动/滑动其实是监听move事件,特点是短时间内要监听多次,那么对于这个事件的绑定函数,即要尽可能精简,避免事件的时间呈指数型增长。那么对于这种高频操作则需要使用防抖和节流,作用就是尽量减少频率从而减少所需的时间。其次,对应涉及到dom操作时,要对dom操作的添加和删除做更少的修改。(虚拟dom和dom diff

虚拟长列表

如果有1w条的长数据要渲染到页面上,如何能保证页面不卡顿?

=> 核心思想还是减少dom的增删操作。假设页面上有10个dom,那么在进行滚动操作时,我可以将dom的内容进行替换,但是不对dom结构进行任何改变操作,给用户一种在滚动的错觉。

截屏2024-09-28 21.53.02.png