前端小白的成长之路 前端系列—代码性能调优

309 阅读3分钟

测试工具

工欲善其事,必先利其器。关于js代码片段的测试,长期以来大家都是通过console.timeEnd类似的方式记录时间来完成的。

这里也纠正上述方式可靠性,我们初中学习对照实验时,知道通过控制变量法才能验证某一变量对实验结果的影响。测试代码也应该如此。js所运行的设备和环境,对测试的结果不可谓不大。

道理先讲到这,下面安利工具---jsperf,除了UI有点丑,好像没啥槽点!

调优方向

1.循环优化

image

2.合并DOM操作

image

除了上例外,引起页面重排和回流的操作,也应该尽量合并操作。

触发重排操作

1. 页面初始渲染
2. 添加/删除可见DOM元素
3. 改变元素位置 ----- 定位属性及浮动 osition,float
4. 改变元素尺寸(宽、高、内外边距、边框等----- 盒子模型相关属性height,padding ,margin , display ,border-width ,min-height
5. 改变元素内容(文本或图片等)text-align,line-height,vertival-align,overflow , font-size,font-family,font-weight
6. 改变窗口尺寸
7. 获取元素的offsetWidth、offsetHeight、clientWidth、clientHeight、width、height、scrollTop、scrollHeight,请求了getComputedStyle(), 或者 IE的 currentStyle

触发回流操作

页面中的元素更新外观或风格相关的属性时就会触发重绘,如:background,color,visibility, border-style ,border-radius outline-color,cursor,text-decoration, box-shadow

3. 异步并发优化promise.all

  await axios.get('https://www.baidu.com/')
  await axios.get('https://juejin.cn')

运行时长由两个异步时间相加决定

  const P1 = axios.get('https://www.baidu.com/')
  const P2 = axios.get('https://juejin.cn')
  await Promise.all([P1, P2])

运行时长由两个异步中较长的时间决定

4. 尾递归优化

这里引用阮一峰老师的三句话来描述尾递归

我们知道,函数调用会在内存形成一个"调用记录",又称"调用帧"(call frame),保存调用位置和内部变量等信息。如果在函数A的内部调用函数B,那么在A的调用记录上方,还会形成一个B的调用记录。等到B运行结束,将结果返回到A,B的调用记录才会消失。如果函数B内部还调用函数C,那就还有一个C的调用记录栈,以此类推。所有的调用记录,就形成一个"调用栈"。

尾调用由于是函数的最后一步操作,所以不需要保留外层函数的调用记录,因为调用位置、内部变量等信息都不会再用到了,只要直接用内层函数的调用记录,取代外层函数的调用记录就可以了。

函数调用自身,称为递归。如果尾调用自身,就称为尾递归。

注意:函数最后return的只能是一个单纯的函数调用,函数调用不能再与其他表达式做计算

const cumulative = (n) => {
  if (n === 1) return 1;
  return cumulative(n - 1) + n;
}

优化后:

const cumulative = (n, total) => {
  if (n === 1) return total + 1;
  return cumulative(n - 1, n + total);
}

5. 对性能消耗大的操作,适当使用防抖节流

例如在监听resize、scroll、mousemove等操作时

window.addEventListener('scroll',() => {
    let timer ;
    let startTime = new Date();
    return () => {
        let curTime = new Date();
        if(curTime - startTime >= 2000){
            timer = setTimeout(() =>{
                ...
            },500);
            startTime = curTime;
        }

    }
}());

你还能做的尝试

突破单线程:webworker

处理密集运算:simd

突破js:WebAssembly

不建议优化的方向

1.原生方法的微观比较

const arr = [];
arr[arr.length] = 1;
arr.push(1);

已经忘记是哪位大佬告诉我,数组往末端添加一个元素,前者比后者快。 对于js现在这样的快速发展,就算当下环境是如此,但是只不定哪个标准就会对其中的方法最优化,探究这样细节的比较,大多数情况下是没有意义的

2.针对单一浏览器或设备的重度优化

观点同上,对单个设备或浏览器的比较优化,殊不知其迭代速度远快于js标准,在这上面花时间同样没有必要

参考

《你所不知道的js》 中卷