Open taoliujun opened 1 year ago
已知浏览器在一帧时间里(默认16.6毫秒)要完成好多工作,其中最耗时的是js脚本执行和页面渲染。如果js脚本耗时太长,那要引起页面渲染掉帧,在用户的体验上就是卡顿。
这里有一个处理用户输入的搜索词语,将结果渲染到一个dom列表上的场景:
import { FC, useMemo, useState } from 'react'; const SearchResults: FC<{ query: string }> = ({ query }) => { const datas = useMemo(() => { return new Array(10000).fill(null).map(() => { return `${query} ${Math.random()}`; }); }, [query]); if (!query) { return null; } return ( <div> <h2>search "{query}" list:</h2> {datas.map((v, k) => { return <p key={k}>{v}</p>; })} </div> ); }; export const Main: FC = () => { const [query, setQuery] = useState(''); return ( <> Search: <input value={query} onChange={(e) => { setQuery(e.target.value); }} /> <SearchResults query={query} /> </> ); };
当用户每次输入一个字符,就会触发SearchResults组件的重新渲染,这个渲染包括datas的重新计算,和dom结构的重新渲染,这个时间远远超过16毫秒,会导致下一个输入值的处理任务一直在等待中,造成卡顿。
SearchResults
datas
React提供了时间切片的模式,这里不详细展开了,允许你在调度任务的过程中安排高优先级的任务,而useDeferredValue就是这个模式的一个hook,它可以延迟更新部分UI
时间切片
useDeferredValue
在之前的代码中,我们稍作修改:
//... const [query, setQuery] = useState(''); const defreredQuery = useDeferredValue(query); //... <SearchResults query={defreredQuery} />
当用户快速输入一个字符时,SearchResults组件的渲染就会被延迟,这样就尽量减少卡顿了。
useDeferredValue通过延迟状态的更新来实现这个目的,它不同于节流或防抖的固定时间控制,而是根据一系列复杂调度算法来决定延迟的时间,这样可以尽量减少卡顿的发生。
欢迎大家讨论
一个卡顿场景
已知浏览器在一帧时间里(默认16.6毫秒)要完成好多工作,其中最耗时的是js脚本执行和页面渲染。如果js脚本耗时太长,那要引起页面渲染掉帧,在用户的体验上就是卡顿。
这里有一个处理用户输入的搜索词语,将结果渲染到一个dom列表上的场景:
当用户每次输入一个字符,就会触发
SearchResults
组件的重新渲染,这个渲染包括datas
的重新计算,和dom结构的重新渲染,这个时间远远超过16毫秒,会导致下一个输入值的处理任务一直在等待中,造成卡顿。useDeferredValue
React提供了
时间切片
的模式,这里不详细展开了,允许你在调度任务的过程中安排高优先级的任务,而useDeferredValue
就是这个模式的一个hook,它可以延迟更新部分UI在之前的代码中,我们稍作修改:
当用户快速输入一个字符时,
SearchResults
组件的渲染就会被延迟,这样就尽量减少卡顿了。useDeferredValue
通过延迟状态的更新来实现这个目的,它不同于节流或防抖的固定时间控制,而是根据一系列复杂调度算法来决定延迟的时间,这样可以尽量减少卡顿的发生。