React Recharts 使用大量数据呈现阻塞

Ben*_*ams 7 javascript performance typescript reactjs recharts

我有一个 Recharts 用例,我渲染了超过 20,000 个数据点,这导致了阻塞渲染:

在 CodeSandbox 上查看

(CodeSandbox 有一个小的脉冲动画,因此在创建新图表数据时更容易看到阻塞渲染。)

在使用开发工具衡量性能时,很明显,造成这种情况的原因不是浏览器paintingrendering活动,而是 Recharts 的scripting活动:

表现

我绝不想在这里责怪 Recharts,20k 点很多,但有没有人知道是否有办法绕过阻塞渲染?

我尝试过的事情:

1.) 增量加载

增量加载更多数据(例如 2k + 2k + 2k + ... = 20k),这只会导致更多、更小的渲染阻塞时刻。

2.) 渲染前加载动画

在渲染组件中添加了一个小的布尔状态来跟踪“挂载”状态,它至少会在图表组件挂载时显示加载动画,因此用户不会等待空白页面/路由切换:

const [showLoading, setShowLoading] = useState<boolean>(true);
const { isLoading, data } = useXY()   // remote data fetching
const [isMounted, setIsMounted] = useState(false);

useEffect(() => {
  setIsMounted(true);
}, []);

useEffect(() => {
  setShowLoading(isLoading || !isMounted);
}, [isLoading, isMounted]);

...

if (showLoading) return <LoadingAnimation />

return <div>...chart...</div>

Run Code Online (Sandbox Code Playgroud)

图表 代码:(完整代码见CodeSandbox)

const [showLoading, setShowLoading] = useState<boolean>(true);
const { isLoading, data } = useXY()   // remote data fetching
const [isMounted, setIsMounted] = useState(false);

useEffect(() => {
  setIsMounted(true);
}, []);

useEffect(() => {
  setShowLoading(isLoading || !isMounted);
}, [isLoading, isMounted]);

...

if (showLoading) return <LoadingAnimation />

return <div>...chart...</div>

Run Code Online (Sandbox Code Playgroud)

dec*_*ele 4

您可以使用(当前处于实验阶段)React Concurrent Mode。在并发模式下,渲染是非阻塞的。

export default function App() {
  // imagine data coming from an async request
  const [data, setData] = useState<Data>(() => createData());
  const [startTransition, isPending] = unstable_useTransition();
  function handleNoneBlockingClick() {
    startTransition(() => setData(createData()));
  }
  function handleBlockingClick() {
    setData(createData());
  }
  return (
    <div className="App">
      <button onClick={handleNoneBlockingClick}>
        (None blocking) Regenerate data
      </button>
      <button onClick={handleBlockingClick}>(Blocking) Regenerate data</button>
      {isPending && <div>...pending</div>}
      {data && (
        <>
          <p>
            Number of data points to render:{" "}
            {useMemo(
              () =>
                data.lines.reduce((acc, item) => {
                  return acc + item.data.length;
                }, 0),
              [data.lines]
            ) +
              useMemo(
                () =>
                  data.areas.reduce((acc, item) => {
                    return acc + item.data.length;
                  }, 0),
                [data.areas]
              )}
          </p>
          <Animation />
          <Chart data={data} />
        </>
      )}
    </div>
  );
}
Run Code Online (Sandbox Code Playgroud)

在此示例中,我使用新的 stable_useTransition 挂钩,并在单击按钮时启动Transition,以对图表数据进行无阻塞计算。

动画不是完美的 60fps,但网站仍然响应良好!

查看代码的此分支中的差异:

https://codesandbox.io/s/concurrent-mode-recharts-render-blocking-forked-m62kf?file=/src/App.tsx