requestAnimationFrame中使用耗时的任务,导致部分呈现帧、掉帧,导致帧率FPS下降

浏览器渲染过程

  • 主线程 将 html 文件转化为浏览器能够读懂的 DOM 树 结构。其中会通过网络进程 加载次级资源 ,遇到 js 会停止构建 DOM 树,并执行 js。
  • 主线程 将 css 文件转化为浏览器能够读懂的 styleSheets 结构,并将其中的属性标准化 ,最后计算每个节点的样式
  • 主线程 通过得到的 DOM 树和 styleSheets 样式表合成一颗布局树 并计算每个节点的具体位置
  • 主线程 通过得到的布局树进行图层分层并得到一个图层树
  • 主线程 通过分层树对每一个图层分解绘制指令,得到一个绘制指令列表
  • 合成线程 对图层进行分块处理,并对视口区域内的图块进行位图 转换,将得到的结果通过 GPU 进程 存入到 GPU 显存中。
  • 合成线程 收集位图信息创建合成帧,并将消息通过 IPC 协议传给浏览器主进程,主进程收到消息后,会将页面内容绘制到内存中,最后再将内存显示在屏幕上。

chrome性能面板 Frames

Frames 区域展示了绘制一个帧所花费的确切时间;在这个部分,同样用颜色来区分了四种类型的帧:

  1. 空闲帧(白色):表示在该帧中没有发生任何渲染或更新。
  2. 正常帧(绿色):表示该帧在适当的时间内被渲染出来,没有出现延迟或问题。
  3. 部分呈现帧(黄色,带有稀疏的宽虚线图案):表示 Chrome 尽力在适当的时间内渲染了部分视觉更新。例如:渲染器进程的主线程的工作延迟了,但合成器线程(如滚动)按时完成了渲染,则会出现这种情况。
  4. 丢帧(红色,带有密集的实线图案):表示该帧由于性能问题或其他原因导致延迟,无法按时渲染。

JS引擎和渲染引擎

渲染引擎一般是1秒渲染60次,即1000ms/60=16.667ms渲染一次。

JS引擎和渲染引擎是互斥的,同时只能一个执行。

当JS引擎执行完一个宏任务和微任务队列中的所有微任务后,会检查下是否需要渲染,需要的会就会将控制权交给渲染线程。如果JS执行时间太长,超出16ms,会导致渲染引擎达不到1秒60次的渲染频率。

测试案例1:

js 复制代码
<body>
    <div id="test"></div>
    <script>
       let i = 0
       let lastTime = 0
       function loop() {
            window.requestAnimationFrame(function(rafTime) {
                console.log("requestAnimationFrame调用间隔:", rafTime - lastTime)
                lastTime = rafTime
                document.getElementById('test').innerText += 'a'
               if(i++ < 10000) loop()
            })
       }
       loop()
    </script>
</body>

测试案例2:

在window.requestAnimationFrame()的回调函数中增加耗时任务,导致帧率FPS下降至37,导致执行window.requestAnimationFrame()的频率下降,导致频繁出现部分呈现帧

js 复制代码
<body>
    <div id="test"></div>
    <script>
       let i = 0
       let lastTime = 0
       function loop() {
        
        window.requestAnimationFrame(function(rafTime) {
            console.log("requestAnimationFrame调用间隔:", rafTime - lastTime)
            lastTime = rafTime
            document.getElementById('test').innerText += 'a'
            for (let index = 0; index < 2000; index++) {
                console.log(i)
                
            }
           if(i++ < 10000) loop()
        })
       }
       loop()
    </script>
</body>

测试案例3:

继续加长耗时任务,帧率FPS下降至21,window.requestAnimationFrame()的执行间隔增长至66~82ms,频繁出现部分呈现帧,频繁掉帧

js 复制代码
<body>
    <div id="test"></div>
    <script>
       let i = 0
       let lastTime = 0
       function loop() {
        
        window.requestAnimationFrame(function(rafTime) {
            console.log("requestAnimationFrame调用间隔:", rafTime - lastTime)
            lastTime = rafTime
            document.getElementById('test').innerText += 'a'
            for (let index = 0; index < 5000; index++) {
                console.log(i)
                
            }
           if(i++ < 10000) loop()
        })
       }
       loop()
    </script>
</body>
相关推荐
天天扭码1 分钟前
深入解析 JavaScript 中的每一类函数:从语法到对比,全面掌握适用场景
前端·javascript·面试
北上ing1 小时前
同一页面下动态加载内容的两种方式:AJAX与iframe
前端·javascript·ajax
纪元A梦1 小时前
华为OD机试真题——推荐多样性(2025A卷:200分)Java/python/JavaScript/C++/C语言/GO六种最佳实现
java·javascript·c++·python·华为od·go·华为od机试题
小墨宝2 小时前
js 生成pdf 并上传文件
前端·javascript·pdf
www_pp_3 小时前
# 构建词汇表:自然语言处理中的关键步骤
前端·javascript·自然语言处理·easyui
YuShiYue3 小时前
pnpm monoreop 打包时 node_modules 内部包 typescript 不能推导出类型报错
javascript·vue.js·typescript·pnpm
天天扭码3 小时前
总所周知,JavaScript中有很多函数定义方式,如何“因地制宜”?(ˉ﹃ˉ)
前端·javascript·面试
一个专注写代码的程序媛3 小时前
为什么vue的key值,不用index?
前端·javascript·vue.js
장숙혜4 小时前
ElementUi的Dropdown下拉菜单的详细介绍及使用
前端·javascript·vue.js
火柴盒zhang4 小时前
websheet之 编辑器
开发语言·前端·javascript·编辑器·spreadsheet·websheet