Appearance
性能优化
性能优化的题目也是面试常考的,这类题目有很大的扩展性,能够扩展出来很多小细节,而且对个人的技术视野和业务能力有很大的挑战。这部分笔者会重点讲下常用的性能优化方案。
题目:总结前端性能优化的解决方案
优化原则和方向
性能优化的原则是以更好的用户体验为标准,具体就是实现下面的目标:
- 多使用内存、缓存或者其他方法
- 减少 CPU 和 GPU 计算,更快展现
优化的方向有两个:
- 减少页面体积,提升网络加载
- 优化页面渲染
减少页面体积,提升网络加载
- 静态资源的压缩合并(JS 代码压缩合并、CSS 代码压缩合并、雪碧图)
- 静态资源缓存(资源名称加 MD5 戳)
- 使用 CDN 让资源加载更快
优化页面渲染
- CSS 放前面,JS 放后面
- 懒加载(图片懒加载、下拉加载更多)
- 减少 DOM 查询,对 DOM 查询做缓存
- 减少 DOM 操作,多个操作尽量合并在一起执行(
DocumentFragment
) - 事件节流
- 尽早执行操作(
DOMContentLoaded
) - 使用 SSR 后端渲染,数据直接输出到 HTML 中,减少浏览器使用 JS 模板渲染页面 HTML 的时间
详细解释
静态资源的压缩合并
如果不合并,每个都会走一遍之前介绍的请求过程
<script src="a.js"></script>
<script src="b.js"></script>
<script src="c.js"></script>
1
2
3
4
2
3
4
如果合并了,就只走一遍请求过程
<script src="abc.js"></script>
1
2
2
静态资源缓存
通过链接名称控制缓存
<script src="abc_1.js"></script>
1
2
2
只有内容改变的时候,链接名称才会改变
<script src="abc_2.js"></script>
1
2
2
这个名称不用手动改,可通过前端构建工具根据文件内容,为文件名称添加 MD5 后缀。
使用 CDN 让资源加载更快
CDN 会提供专业的加载优化方案,静态资源要尽量放在 CDN 上。例如:
<script src="https://cdn.bootcss.com/zepto/1.0rc1/zepto.min.js"></script>
1
2
2
使用 SSR 后端渲染
可一次性输出 HTML 内容,不用在页面渲染完成之后,再通过 Ajax 加载数据、再渲染。例如使用 smarty、Vue SSR 等。
CSS 放前面,JS 放后面
上文讲述浏览器渲染过程时已经提过,不再赘述。
懒加载
一开始先给为 src
赋值成一个通用的预览图,下拉时候再动态赋值成正式的图片。如下,preview.png
是预览图片,比较小,加载很快,而且很多图片都共用这个preview.png
,加载一次即可。待页面下拉,图片显示出来时,再去替换src
为data-realsrc
的值。
<img src="preview.png" data-realsrc="abc.png"/>
1
2
2
另外,这里为何要用data-
开头的属性值?—— 所有 HTML 中自定义的属性,都应该用data-
开头,因为data-
开头的属性浏览器渲染的时候会忽略掉,提高渲染性能。
DOM 查询做缓存
两段代码做一下对比:
var pList = document.getElementsByTagName('p') // 只查询一个 DOM ,缓存在 pList 中了
var i
for (i = 0; i < pList.length; i++) {
}
1
2
3
4
5
2
3
4
5
var i
for (i = 0; i < document.getElementsByTagName('p').length; i++) { // 每次循环,都会查询 DOM ,耗费性能
}
1
2
3
4
2
3
4
总结:DOM 操作,无论查询还是修改,都是非常耗费性能的,应尽量减少。
合并 DOM 插入
DOM 操作是非常耗费性能的,因此插入多个标签时,先插入 Fragment 然后再统一插入 DOM。
var listNode = document.getElementById('list')
// 要插入 10 个 li 标签
var frag = document.createDocumentFragment();
var x, li;
for(x = 0; x < 10; x++) {
li = document.createElement("li");
li.innerHTML = "List item " + x;
frag.appendChild(li); // 先放在 frag 中,最后一次性插入到 DOM 结构中。
}
listNode.appendChild(frag);
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
事件节流
例如要在文字改变时触发一个 change 事件,通过 keyup 来监听。使用节流。
var textarea = document.getElementById('text')
var timeoutId
textarea.addEventListener('keyup', function () {
if (timeoutId) {
clearTimeout(timeoutId)
}
timeoutId = setTimeout(function () {
// 触发 change 事件
}, 100)
})
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
尽早执行操作
window.addEventListener('load', function () {
// 页面的全部资源加载完才会执行,包括图片、视频等
})
document.addEventListener('DOMContentLoaded', function () {
// DOM 渲染完即可执行,此时图片、视频还可能没有加载完
})
1
2
3
4
5
6
7
2
3
4
5
6
7
性能优化怎么做
上面提到的都是性能优化的单个点,性能优化项目具体实施起来,应该按照下面步骤推进:
- 建立性能数据收集平台,摸底当前性能数据,通过性能打点,将上述整个页面打开过程消耗时间记录下来
- 分析耗时较长时间段原因,寻找优化点,确定优化目标
- 开始优化
- 通过数据收集平台记录优化效果
- 不断调整优化点和预期目标,循环 2~4 步骤
性能优化是个长期的事情,不是一蹴而就的,应该本着先摸底、再分析、后优化的原则逐步来做。