流数据渲染优化
12/2/2025, 12:51:38 PM modified by Marvin结合懒加载与增量渲染优化流式数据体验。
更新于 2024-06-06。原文附带 React Demo,此处保留核心原理与伪代码。
切入点
如何对流式数据做优化以提升渲染性能,可从两点切入:
懒加载渲染:可视区外的内容暂不渲染,滚动到可视区时再渲染。
注意:需要缓冲区防止给用户造成流暂停假象。
增量式渲染:已渲染到页面的数据不重复渲染,只渲染新增内容。
原理详解
懒加载渲染伪代码
let content = '';
let buffer = ''
function updateState(chunk) {
buffer = chunk;
if('in view'){
content += buffer;
buffer = ''
} else if("out of view") {
buffer += chunk
}
requestAnimationFrame(updateState)
}
for (const chunk of "hello world") {
updateState(chunk)
}增量式更新核心代码
import { useCallback, useRef, useEffect } from "react";
import { sleep } from "aio-tool";
export enum PromiseState {
Resume = 'resume',
Suspense = 'suspense',
Cancel = 'cancel'
}
export default function useIncreasingRender({
onContinue,
}: {
onContinue?: (value: string) => void;
}) {
// Ref to control the consumer flow
const promiseRef = useRef<PromiseState | Function>(PromiseState.Cancel);
// Buffer for accumulating incoming characters
const remainRef = useRef<string>("");
// Store the requestAnimationFrame handle so it can be canceled when needed
const renderLoopRef = useRef<number | null>(null);
const updater = useCallback(async () => {
if (promiseRef.current === PromiseState.Resume) {
onContinue?.(remainRef.current);
remainRef.current = "";
} else if (promiseRef.current === PromiseState.Suspense) {
// Wait for an external signal to resume
await new Promise((resolve) => {
promiseRef.current = () => {
resolve(true);
};
});
}
// Schedule next iteration in the next animation frame
renderLoopRef.current = requestAnimationFrame(updater);
}, [onContinue]);
const cancel = useCallback(() => {
if (renderLoopRef.current !== null) {
cancelAnimationFrame(renderLoopRef.current);
renderLoopRef.current = null;
}
remainRef.current = "";
promiseRef.current = PromiseState.Cancel;
}, []);
const start = useCallback(() => {
promiseRef.current = PromiseState.Resume;
updater().then();
}, [updater]);
const consume = useCallback(async (value: string) => {
if (promiseRef.current === PromiseState.Cancel) return true;
remainRef.current += value;
await sleep(0);
}, []);
useEffect(() => cancel, [cancel]);
return {
start,
cancel,
consume,
promiseRef,
remainRef,
};
}Git Commit History(1 commits)
fix: 简化zod form
Marvin
12月2日 12:51
e0cadf68