【问题标题】:How can I make React as CPU efficient as vanilla JS?我怎样才能让 React 像 vanilla JS 一样高效?
【发布时间】:2020-06-27 05:33:51
【问题描述】:

我有一个简单的脚本,它每千分之一秒更新一个数字。如果我用 vanilla(标准)JS 写出来并运行它,它会消耗大约 40% 的 CPU(在所有 4 个内核上)。如果我在 React 中实现一个类似的功能,它会消耗我大约 60% 的 CPU,并且在至少一个内核上持续达到 100% 的峰值。

这里是代码。

在原版 JS (https://codesandbox.io/s/headless-dew-12vli)

index.html

<body>
    <h1>Vanilla JS</h1>
    <div>
      <div>Counter: <span class="counter">1</span></div>
      <div>Counter: <span class="counter">2</span></div>
      <div>Counter: <span class="counter">3</span></div>
      <div>Counter: <span class="counter">4</span></div>
      <div>Counter: <span class="counter">5</span></div>
      <div>Counter: <span class="counter">6</span></div>
      <div>Counter: <span class="counter">7</span></div>
      <div>Counter: <span class="counter">8</span></div>
      <div>Counter: <span class="counter">9</span></div>
      <div>Counter: <span class="counter">10</span></div>
    </div>

    <script src="src/index.js"></script>
  </body>

script.js

const list = document.getElementsByClassName("counter");

for (let item of list) {
  setInterval(() => {
    item.innerHTML = Number(item.innerHTML) + 0.001;
  }, 1);
}

在 React 中 (https://codesandbox.io/s/upbeat-wood-puhdu)

App.js

import React from "react";
import Counter from "./Counter";

export default function App() {
  return (
    <div className="App">
      <h1>React</h1>
      <Counter start="1" />
      <Counter start="2" />
      <Counter start="3" />
      <Counter start="4" />
      <Counter start="5" />
      <Counter start="6" />
      <Counter start="7" />
      <Counter start="8" />
      <Counter start="9" />
      <Counter start="10" />
    </div>
  );
}

Counter.js

import React from "react";
import { useEffect, useState } from "react";

export default function Counter(props) {
  const [amt, setAmt] = useState(parseInt(props.start, 10));

  useEffect(() => {
    // setAmt(parseInt(props.start));
    const interval = setInterval(() => {
      setAmt(amt => amt + 0.001);
    }, 1);
    return () => clearInterval(interval);
  }, []);

  return (
    <>
      <div>
        counter:{" "}
        <span id={props.start} className="counter">
          {amt}
        </span>
      </div>
    </>
  );
}

将其降低到 1/100 秒可以稍微控制一些事情,但 React 仍然比普通 JS 更占用 CPU...

两个问题:

  1. 如何让这个 react 组件像它的类似 JavaScript 脚本一样高效?
  2. 总的来说,我怎样才能使这个脚本更高效?

【问题讨论】:

  • @user120242 反应键仅在渲染元素列表时才真正有用,请参阅Lists & Keys。对于 OP,react 已经真正优化了。尽可能多地用于 DOM 差异和状态管理的整个框架。请参阅reconciliationOptimizing performance。您是否正在尝试解决更深层次的潜在性能问题,或者只是探索 React 性能?
  • 我在我正在处理的一个与 CPU 峰值有关的项目中注意到了这一点。我希望弄清楚如何解决它,因此我是一个体贴的开发人员,为我的最终用户和他们的功耗提供服务。
  • 能成为一个体贴的开发人员真是太好了,但这感觉是边缘化的过早优化。我的猜测是尖峰只是在做它的事情,即状态更新,因此它正在区分 virtualDOM 并向实际 DOM 提交(刷新)更新。如果您以每秒数百/数千次的速度更新状态,我会期待这一点。您正在定义 很多 匿名回调函数,因此它们可能会构建并由浏览器批量收集垃圾。
  • 好吧,更具体地说,在项目中我得到了 60% 的 cpu 使用率,导致风扇在我的机器上旋转......我将问题提炼到这个例子中,看看它是否是屏幕更新还是基础计算...两者都有,我敢肯定,但上述差异并非微不足道。
  • 注意:你总是可以编写 vanilla JS 来超越 React。根据定义。就像你总是可以编写 asm 来超越其他任何东西一样。您不太可能做到(除了人为的示例和特定领域,即使那样差异很可能可以忽略不计),并且代码质量和生产力很可能会受到影响(随着代码的复杂性增加)。

标签: javascript reactjs performance optimization


【解决方案1】:

首先,不要期望 setInterval 实际上每毫秒调用一次。通常不会。在我的机器上,我得到 253,但你的可能不同。

//Logs the number of calls done in one second
let counter = 0
let interval = setInterval(function() {counter++}, 1)
setTimeout(function(){clearInterval(interval);console.log(counter);counter=0}, 1000)

其次,抽象,就像 ReactJS 所做的那样,只会让代码变慢——假设你写的很完美。通过进行额外的函数调用,您永远不会获得速度提升。您从库中获得的是易于使用、优化良好的代码以及广泛兼容的代码,这一切都无需您付出很大的努力。

不要沉迷于性能。这段代码每秒编辑 10k 个元素,并且只使用了 40% 的 CPU。很少有网页每秒编辑 10k 个元素,而那些这样做的网页可能会使用渐进式渲染来防止您注意到性能问题。

最后,如果您的目标是做一个计时器,请使用 Date.now() 以便主机上的一点延迟不会导致问题。此外,仅在有用的情况下运行您的代码,或者在您的情况下,每帧运行一次,尝试使用 window.requestAnimationFrame() 或 setInterval(function, 16)

【讨论】:

  • developer.mozilla.org/en-US/docs/Web/API/… 超时/间隔被限制为 >= 4ms
  • 有趣的提示:window.request.AnimationFrame()。在现实世界中,它会以更现实的时间间隔更新,但我把它发挥到了极致以放大问题......
  • 您的极端样本与大多数微基准具有相同的问题。它不再测试与真正瓶颈相关的内容。您可能应该澄清您正在为哪些用例进行这些测试。
  • @Trees4theForest,在这种情况下,放大问题只会产生一个以前没有的问题。与计算机一样快,每秒 10k 次修改 DOM 预计会对性能产生影响 - 当您每秒处理 10k 次操作时,即使是一点点开销也会很快增加。
猜你喜欢
  • 1970-01-01
  • 2021-11-06
  • 2018-02-02
  • 2014-09-07
  • 2015-09-23
  • 1970-01-01
  • 1970-01-01
  • 2013-08-25
  • 1970-01-01
相关资源
最近更新 更多