• 从这个博客皮肤迈入前端性能优化一小步


    GZ/awescnb

    前置

    正如你所见,我现在用的这个博客皮肤,在没优化之前帧率会降到个位数. 现在与之相比,是不是好很多呀? 下面将从滚动 scroll 优化这一方面展开,主要说一下思路.

    只在极少情况下会降到 30fps,一般稳定在 55-60fps.

    头部导航条

    头部导航条会监听滚动条上下滚动的方向随之展开或隐藏.当隐藏时,文章目录会上移一小段距离并固定;反之,会回到原来的位置.这里的头部导航条使用了 css3 transform 属性, 有时会调动 GPU 来加速, 所以导航条优化主要在于对监听滚动条事件的处理.

    不好的做法

    头部导航条和文章目录各监听一个事件,分别写在一个 func 中. 这样会增加一个事件消耗,且代码会有冗余.

    优化

    仔细分析, 文章目录是随头部导航条的变化而变化的.完全可以做一次事件监听完成这两件事,例如下面这样:

    $(window).scroll(function() {
      if (condition) {
        // 显示导航条
        // 目录下移
      } else {
        // 隐藏导航条
        // 目录上移
      }
    })
    

    因为代码量比较大, 当时写的时候就没考虑合并, 主要是先把功能做出来.
    有了经验,以后再遇到类似的情况,就会考虑他们之间的联系了, 这里的重点就是找到这两者之间的联系.
    但是这样就完了吗 ? 有些经验的, 可能想到防抖(debounce), 但仔细想想防抖真的够好吗?
    如果加上防抖, 只会降低在一段时间内事件触发的频率.这样能减少很大的性能开销,一般遇到监听 scroll 或者 resize 等事件首先都会这样想吧.
    仔细观察,就可以发现: 如果一直向上或向下滚动,导航条和文章目录都会保持一个状态, 只有逆向滚动时,它们才会一起发生一次状态改变.
    这里有更好的做法:

    function scrollFunc() {
      let scrollDirection
      if (!scrollAction) {
        scrollAction = window.pageYOffset
      }
      let diff = scrollAction - window.pageYOffset
      if (diff < 0) {
        scrollDirection = "down"
      } else if (diff > 0) {
        scrollDirection = "up"
      }
      scrollAction = window.pageYOffset
      return scrollDirection
    }
    
    let scrollAction, originalDir
    
    $(window).scroll(function() {
      let direction = scrollFunc()
      if (direction && originalDir != direction) {
        if (direction == "down") {
          // 显示导航条
          // 目录下移
        } else {
          // 隐藏导航条
          // 目录上移
        }
        originalDir = direction
      }
    })
    

    上面这段代码,很容易明白.概括一下: 相当于增加一个做更少计算的中间层,只有符合条件时才会触发真正需要执行的操作.
    甚至想让给这个所谓的中间层加上防抖也是可以的.这样和原来的对比更加明显了.

    文章目录活跃标题样式

    监听滚动条滚动,如果这个文章标题超出了顶部, 即认为当前标题下的内容活跃(你正在浏览这部分),就会给当前目录中的标题添加活跃样式.
    很显然,这里涉及遍历操作,所以计算量较大.这里可以使用防抖来优化,但是我使用了节流.使用节流能保证你较快速滚动页面时,依然能触发指定频次的 func,以显示目录活跃状态.
    这样看起来就更平滑.使用防抖则不能,会等你停止滑动瞬间移动给最后一个活跃的标题添加样式,这样有明显的顿感.当然使用防抖也是可以的.很显然,这里使用节流仍然会增加部分开销.
    另外,有更好的方法实现文章目录,比如使用 canvas 来绘制.我写的代码有些糟,可以另做一番优化.

    另外补充一个小知识, 如何判断文章标题是否超出了顶部呢 ? 我是这样实现的:

    getClientRect(element) {
        const {top, bottom, left, right, height, width} = element.getBoundingClientRect()
        return {
            top,
            bottom,
            left,
            right,
            height: height || bottom - top,
             width || right - left
        }
    }
    

    如果你对 Element.getBoundingClientRect() 没有了解, 如果有兴趣, 我在这里放了一个链接-MDN, 你可以跳转以学习它.

    百分比的指示器

    例如右下角带百分比的指示器,通过监听滚动条位置转化成百分比,同时改变元素高度, 以控制动画的高度. 这里就犯了大忌了, 不断改变元素高度, 会导致不断重绘. 这部分使用 requestAnimationFrame 来优化, 虽然帧数提升明显, 这样仍然是极不好的做法, 不要在监听滚动的事件中修改样式! 找了很久没找到能够不改变高度就能实现这个效果的方式, 就给这个皮肤添加了一个可以选择简易指示器的选项.

    back2top: {
        enable: true,
        type: "complex", // 可选 'simple' 不使用动画效果
        right: ""
    },
    

    这个指示器的配置为什么叫 back2top ? 这是因为如果你将鼠标放上去,它会显示一个箭头,点击可以回到顶部,它其实是一个返回顶部的按钮.
    如果你对 window.requestAnimationFrame 没有了解, 如果有兴趣, 我在这里放了一个链接-MDN, 你可以跳转以学习它.

    最后

    博客还有其他地方类似优化, 思路重复就不一一列举了.

    其实完全可以删去很多滚动监听事件, 这样也好, 能够稍微锻炼一下自己, 稍微增加这方面经验.

    文章如有错误不足,敬请指出,谢谢!

  • 相关阅读:
    使用jquery.js写可增行删行可编辑的table
    当Ext.js中xtype: 'checkboxfield'时,没勾选则向后台发送的数据没有字段的解决方法
    沉迷js不能自拔~
    kubernetes concepts -- Pod Overview
    kubernetes concepts (一)
    所有锁的unlock要放到try{}finally{}里,不然发生异常返回就丢了unlock了
    Java故障定位方法总结
    年度计划
    minikube 设置CPU和内存
    Ubuntu 设置中文输入法
  • 原文地址:https://www.cnblogs.com/guangzan/p/12515742.html
Copyright © 2020-2023  润新知