前端性能优化小结

2024-03-12 10:42:15 浏览数 (1)

一个老生常谈的问题,算是一项任重而道远的工程,总的来看优化范围很广,但我们可以从单个点逐步切入,养成自动代入优化思维才是目的,这里只做简单记录一下。

问题方案

切入主题,综合资料总的来看,一般直接造成页面卡顿的原因主要有 大批量dom操作无限制监听函数大量源站请求(懒加载、CDN、雪碧图、字体图标、浏览器缓存等)都可造成页面可视范围延迟卡顿,大部分问题网上早有解决方案,主要还是需要改写部分代码的习惯,简单聊下优化范围(部分参考资料来自互联网整合,来源均标注于文尾),详细优化方案可参考:让你的网页更丝滑(全)

  • dom操作重排/绘(排版布局动画实现)
  • 函数监听机制(函数执行次数限制)
  • Promise / Web Worker、Time Slicing(延迟执行,队列任务,线程阻塞)

DOM操作

一般来说dom操作对页面卡顿影响虽不是最大但肯定是最常见的,习惯使用 jquery 的小伙伴肯定不陌生,包括原生 js 也是一样,错误的操作会导致页面重排造成非常大的性能消耗!所以首先要优化操作,网上已经有很多详细优化方案

页面渲染流程

DOM 操作会导致最重要的,也是我们最需要的问题就是导致用户阻塞的重构 (reflow) 和重绘 (repaint) 比较通俗的一句话就是你在页面上的任何操作都是有代价的,有些大有些小,如果我们的操作比较频繁或者波及范围较大,那么就要讲究方式和技巧 reflow 和 repaint 就是我们在改变页面或者说操作 DOM 时,会带来的两种后果 reflow 意味着结构的改变,比如一堆元素堆叠,改变其中一个的宽高,那么相应的所有元素的位置都要改变.repaint 意味着样式的改变比如 div 调整了背景色等,但是位置不变,只改变我们操作的元素。所以通常来看 repaint 的代价要远小于 reflow, 速度也更快

在 CSS 中可使用 transform opacity 属性更改来实现动画,这两个属性更改不会触发重排与重绘,它们是可以由合成器(composite)单独处理的属性。

  • tips1:CSS使用 translate替代position位移,使用 transform:translateZ(0)will-change: ***;新建渲染图层,强制开启GPU加速(注意:不要创建过多的渲染层,这意味着新的内存分配和更复杂的层管理)
  • tips2:JS使用 requestAnimationFrame(fn) 替代传统 setTimeout/setInterval 定时器动画

DOM渲染顺序(选择性渲染Layout及Paint)tips:gif图片会持续触发 Paint

能放到 DOM 操作之外的操作就放到外面,DOM 操作要尽量少

DOM 操作优化中这一观点在网上已经很普及了,很多例子都有比如遍历一个数组然后逐渐把内容添加到 DOM 上,这里就推荐先遍历完数组,然后一次性在 DOM 上操作。大家可以看代码:

代码语言:javascript复制
// 不好
for (var i=0; i < items.length; i  ){
    var item = document.createElement("li");
    item.appendChild(document.createTextNode("Option "   i);
    list.appendChild(item);
}
// 好 使用容器存放临时变更, 最后再一次性更新DOM
var fragment = document.createDocumentFragment();
for (var i=0; i < items.length; i  ){
    var item = document.createElement("li");
    item.appendChild(document.createTextNode("Option "   i);
    fragment.appendChild(item);
}
list.appendChild(fragment);

大范围操作先把容器隐藏,在其中操作完成后,再显示

这是一个我刚接触前端时遇到的一个优化办法,当时很不理解为什么 display=none 之后操作就算是性能优化了。但是数据证明如此渲染确实快了很多,这个的原理要涉及到浏览器加载和渲染的原理,简单说就是隐藏的元素其中不会产生 reflow. 这个例子我就不写了,很简单.

样式操作不要注意修改属性,直接替换 class

这个还是比较容易理解的,你逐一修改要访问很多次,而替换 class 就相当于批量操作了,访问一次 DOM 就可以了,当然性能提高了.

用变量保存 DOM 对象而不是多次获取,同时减少操作 DOM 属性的次数

代码语言:javascript复制
//不好
function addAnchor(parentElement, anchorText, anchorClass) {
  var element = document.createElement('a');
  parentElement.appendChild(element);
  // 下方额外多了两次渲染
  element.innerHTML = anchorText;
  element.className = anchorClass;
}
//好
function addAnchor(parentElement, anchorText, anchorClass) {
  var element = document.createElement('a');
  element.innerHTML = anchorText;
  element.className = anchorClass;
  parentElement.appendChild(element);
}

函数控制

主要涉及到优化有定时器动画函数防抖节流闭包、减少判断层级减少循环体活动事件绑定、事件队列等,在监听事件时控制函数触发间隔(如滚动对页面性能造成的影响,如可视区懒加载)控制,通过 Promise异步处理 大批量拥有前置条件(可能阻塞页面其他脚本执行序列)的函数操作。应用到实现不限于

  • 可视区懒加载
  • 异步请求回调
代码语言:javascript复制
// 父元素绑定事件,自动向上遍历直到指定 CLASS 类的子元素生效(避免在循环中大量绑定子元素事件)
function bindEventClick(parent){
    parent.onclick=(e)=>{
        e = e || window.event;
        let t = e.target || e.srcElement;
        if(!t) return;
        while(t!=parent){
            if(t.id=="id"){
                // target id..
                break;
            }else if(t.classList.contains("class")){
                // target class..
                break;
            }else if(t.nodeName.toLowerCase()=="body"){
                // target node..
                break;
            }else{
                t = t.parentNode;
            }
        }
    }
}

// 闭包节流器
// 使用被节流的匿名函数作为回调函数
// window.addEventListener("scroll", closure_throttle((e)=>{
//     console.log(e);
// }, 1200));
function closure_throttle(callback=false, delay=200){
    let closure_variable = true;  //default running
    return function(){
        if(!closure_variable) return;  //now running..
        closure_variable = false;  //stop running
        setTimeout(()=>{
            callback.apply(this, arguments);
            closure_variable = true;  //reset running
        }, delay);
    };
}
// 指定被节流函数,可用于移除事件处理器
const scrollFunc= closure_throttle((e)=>{
    console.log(e);
}, 1200);
// 使用被节流的指定函数作为回调函数
window.addEventListener("scroll", scrollFunc);
// 移除事件(若 scrollFunc 为匿名函数则无法正常移除)
// window.removeEventListener('scroll', scrollFunc);

性能监测

Chromium 内核浏览器可通过谷歌 Lighthouse 评估页面测试。

通过F12控制台中的 Rendering 选项卡可检测页面FPS,LFC页面性能参数等。通过 Performance 选项卡可录制查看有红色标记的主线程丢帧情况(元素、原因、时间),具体可参考:浏览器Performance性能监控使用详解、

0 人点赞