title: 关于前端性能优化的一些建议 date: 2021-06-10 18:30:00 updated: 2021-06-10 18:30:00 tags:
本文主要介绍性能优化的一些建议,包括编码,动画性能,页面启动性能优化
使用全局变量和函数要比局部的开销更大,可以将全局变量保存在局部的变量中,再去调用.
将在一个函数中多次调用的全局对象保存到局部变量总是没错的[1]
// 全局查找 document
function updateElement() {
var p = document.getElementsByTagName('p');
var title = document.getElementById('title');
p.forEach(val => {
val.innerHTML = document.title;
})
title.innerHTML = 'world';
}
// 局部查找
function updateElement() {
var doc = document;
var p = doc.getElementsByTagName('p');
var title = doc.getElementById('title');
p.forEach(val => {
val.innerHTML = doc.title;
})
title.innerHTML = 'world';
}
with
语句相信大家都听说过。它会创建自己的作用域,因此会增加其中执行代码的作用域链长度,造成with
语句中执行的代码会比外面执行的代码要慢。
能减少算法的复杂度要尽量减少,尽可能地多使用局部变量将属性查找替换为值查找。
// 代码1
var a = 1;
var b = 100 + a;
console.log(b);
// 代码2
var arr = [1, 100];
var b = arr[0] + arr[1];
console.log(c);
上面两端代码执行效率是一样的
代码1它会进行4次常量查找,分别是:a,数字1,数字100,b,时间复杂度为O(1)
代码2的时间复杂度也是O(1)
若是用对象声明变量查找,他会比在数组上访问变量时间更长,比如
var obj = { a: 1, b: 100 };
var result = obj.b - obj.a;
console.log(result);
上面代码访问变量的时间复杂度为O(n)
,对象在查找时必须向原型链中搜索该属性,所以对象属性越多,查找时间越长。
将循环终止的条件值放到声明变量中,避免属性查找或其他 O(n)的操作
// before
const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0];
for (let i = 0; i < arr.length; i ++) {
...
}
// after
const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0];
for (let i = 0, len = arr.length; i < len; i ++) {
...
}
// 避免!!!
const code = `var a = 0; console.log(a)`;
eval(code);
var test = new Function(code);
setTimeout(code, 1000);
代码语句数量也是会影响执行的操作的速度,完成多个操作的单个语句要比完成单个操作的多个语句快
// before
var a = 1;
var b = 'a';
var c = {};
var d = [];
// after
var a = 1,
b = 'a',
c = {},
d = [];
从上面可以了解到单个操作的多个语句相对而言会比较快,因此在初始化对象或数组是,也可以这样做
// before
var arr = [];
arr[0] = 'aaa';
arr[1] = 'bbb';
arr[2] = 'ccc';
var obj = {};
obj.a = 'aaa';
obj.b = 111;
obj.c = () => {};
// after
var arr = ['aaa', 'bbb', 'ccc'];
var obj = {
a: 'aaa',
b: 111,
c: () => {},
}
浏览器中渲染界面是一个非常消耗性能的事情,所以减少频繁地插入DOM节点显得非常有必要。例如:
var list = document.getElementById('list');
for (let x = 0, max = 10, x < max; x++) {
var li = document.createElement('li');
li.innerHTML = x;
list.appendChild(li);
}
上面的代码会向list
重复插入10个节点,每次插入节点,浏览器都会重新计算一次页面的位置。消耗非常大的性能。解决的办法就是使用createDocumentFragment
var list = document.getElementById('list');
var fragment = document.createDocumentFragment();
for (let x = 0, max = 10, x < max; x++) {
var li = document.createElement('li');
li.innerHTML = x;
fragment.appendChild(li);
}
list.appendChild(fragment);
使用文档片段改进后,只有一次插入节点的操作,有效减少节点更新造成的性能消耗。
除此之外,使用innerHTML
也可以造成差不多的效果,但是字符串拼接会有一定的性能损失。
var list = document.getElementById('list');
var html = '';
for (let x = 0, max = 10, x < max; x++) {
html += `<li>${ x }</li>`;
}
list.innerHTML = html;
由发布-订阅模式可以知道,随着监听器的增加,内存也会增加,使得页面性能变差。
在点击列表事件时,可以利用事件代理来减少监听器的声明。
/**
* <ul id="ul">
* <li id="li_1"></li>
* ...
* <li id="li_n"></li>
* </ul>
*/
// before
var list = document.getElementsByTagName('li');
for(let x = 0, len = list.length; x < len; x++) {
list[x].onclick = () => {
...
}
}
// after
var ul = document.getElementById('#ul');
ul.addEventListener('click', (e) => {
let target = e.target;
switch(target.id) {
case "li_1": .....
case "li_2": .....
}
});
defer
或 async
属性加载。这可以让HTML解析器更高效地处理文档。