优化的原则和方向
前端性能优化的原则其实就是更好的用户体验,具体实现的目标大体有两个:
- 合理使用内存或缓存,减少请求;
- 减少CPU或者GPU的计算,达到更快的展现。
前端在性能优化的方向大体有两个:
- 减少页面体积,提升网络加载
- 优化页面渲染
详情
减少页面体积,提升网络加载
1、静态资源的压缩合并(JS 代码压缩合并、CSS 代码压缩合并、雪碧图)
- 压缩是为了减小文件体积,减轻网络负载,达到更快的下载;
- 合并和雪碧图都是为了减少文件的请求次数,但不是合并的就一个比没有合并时加载快,要看合并之后的体积,若文件合并后太大了也不太利于性能优化,所以在实际的项目中要做好权衡。
2、静态资源缓存(资源名称加 MD5 戳)
可以通过链接名称控制缓存:通过前端构建工具为打包的文件添加md5后缀,这样当打包上线时请求的链接发生改变,这样可以防止由于缓导致静态资源更新失效;
3、 使用 CDN 让资源加载更快
优化页面渲染
1、CSS 放前面,JS 放后面
- 浏览器在渲染解析过程中,若遇到
<link href="...">
和<script src="...">
这种外链加载 CSS 和 JS 的标签,浏览器会异步下载并解析执行。CSS放在头部是为了让浏览器尽早解析执行Css文件,渲染出页面的样式,若放在底部会出现渲染卡顿的情况,影响性能和体验。- 而当渲染过程中遇到script标签时就会执行JS代码,从阻塞页面渲染,因为浏览器渲染和 JS 执行共用一个线程,而且这里必须是单线程操作,多线程会产生渲染 DOM 冲突。所以要将JS放在底部,等到页面渲染完成之后再去解析执行js,保证用户体验性。因为浏览器渲染和 JS 执行共用一个线程,而且这里必须是单线程操作,多线程会产生渲染 DOM 冲突。
- 另外,JS 执行如果涉及 DOM 操作,得等待 DOM 解析完成才行,JS 放在底部执行时,HTML 肯定都解析成了 DOM 结构。JS 如果放在 HTML 顶部,JS 执行的时候 HTML 还没来得及转换为 DOM 结构,可能会报错。
2、懒加载(图片懒加载、下拉加载更多)
先将src赋值成一个通用的预览图,下拉时候再动态赋值成正式的图片。如下,preview.png是预览图片,比较小,加载很快,而且很多图片都共用这个preview.png,加载一次即可。待页面下拉,图片显示出来时,再去替换src为data-src的值。(data-开头的属性浏览器渲染的时候会忽略掉,提高渲染性能)
1 | <img src="preview.png" data-src="realImg.png"/> |
3、减少DOM 查询,对 DOM 查询做缓存
1 | // 只查询一个 DOM ,缓存在 pList 中了 |
4、减少DOM 操作,多个操作尽量合并在一起执行(DocumentFragment)
DOM 操作是非常耗费性能的,因此插入多个标签时,先插入 Fragment 然后再统一插入 DOM。因为Fragment文档片段存在于内存中,并不在DOM树中,所以将子元素插入到文档片段时不会引起页面回流。
1 | var listNode = document.getElementById('list') |
5、事件节流
在开发过程中会遇到页面一些频繁触发的事件,比如mouseover、scroll、resize等事件。一秒可以执行很多次,这样会造成严重的页面性能问题,导致页面c出现卡顿甚至浏览器崩溃。因此我们需要对事件进行节流,简单的说就是控制其执行的次数。这里就涉及到了常用到的js的节流和防抖功能实现。
1、 防抖(debounce):在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时。
1 | function debounce(fn, delay) { |
2、节流(throttle):规定一个单位时间,在这个单位时间内,只能有一次触发事件的回调函数执行,如果在同一个单位时间内某事件被触发多次,只有一次能生效。
1 | function throttle(fn, threshhold) { |
6、尽早执行操作(DOMContentLoaded)
1 | window.addEventListener('load', function () { |
7、使用 预渲染 或者 SSR后端渲染,数据直接输出到 HTML 中,减少浏览器使用 JS 模板渲染页面 HTML 的时间 (如Vue SSR),同时也有利于网站的SEO。