最近,React官方放出了最新特性Concurrent模式(实验阶段)的文档。又是一个非常实用的特性。
什么是并发?官方解释是:并发模式是一些可以帮助React保持响应地和友好地去适应用户设备性能和网速。
也就是React可以根据用户设备的性能和网速做出一些调整,以保证页面能够更流畅的渲染。
React给出了两种并发的应用:数据获取和UI渲染
传统的UI渲染是阻塞式的,在当前任务没有执行完毕时不会去进行下一步的操作,即使有更加紧急的任务也必须等到前面的任务执行完毕。React对此做出了优化,使得UI渲染是可以打断的,在执行Ui渲染时,如果有更加紧急的任务,将会先去执行该任务,等待该任务执行完毕后再去继续执行前面的任务。React官方给出了一个例子:过滤列表的例子。我们在输入框输入内容时,列表就会立刻进行渲染,但渲染是阻塞的,渲染时将不能进行输入,页面会出现卡顿,传统情况下,为了减少渲染次数,我们会用节流和防抖方式进行优化,但是这样还是有些不友好的体验。React引入并发模式后,所有的操作都是可以打断的,我们继续输入的时候,渲染就会被打断,在你输入的空隙就会继续渲染,这样就使得页面的渲染更加流畅。当然这只是非常基本的一种情况,实际上,React还可以做的更多。
React官方把并发模式比作了git的分支操作,React会在主分支上进行操作时,其他的操作可以在其他分支上(后台)完成,等主分支上的任务执行完毕后,就会拿到其它分支上的执行结果进行处理。
在React16.6引入了Suspense,可以优雅的进行异步组件的加载处理。但是现在,Suspense可做的更多。
例如数据的获取:
const resource = fetchProfileData();
function ProfilePage() {
return (
<Suspense fallback={<h1>Loading profile...</h1>}>
<ProfileDetails />
<Suspense fallback={<h1>Loading posts...</h1>}>
<ProfileTimeline />
</Suspense>
</Suspense>
);
}
function ProfileDetails() {
// 尝试去读取用户信息,即使数据还没有获取到
const user = resource.user.read();
return <h1>{user.name}</h1>;
}
function ProfileTimeline() {
// 尝试去帖子列表,即使数据还没有获取到
const posts = resource.posts.read();
return (
<ul>
{posts.map(post => (
<li key={post.id}>{post.text}</li>
))}
</ul>
);
}
这也就是并发模式的数据加载方式;在数据还没有加载到的时候,会通过Susepnse显示一个降级的渲染。注意:这里的加载方式必须使用被封装好的并发式的请求工具函数。React官方给我们介绍了两种:Rely、和一个简单的fake api,具体使用可以去React官网查找。通过这种方式就可以在数据加载的时候优雅地提供一个过渡状态。
另一个是UI渲染的并发模式,我们可以在例如按钮点击时不去立刻进行操作(比如进入一个空白页面),而是给他一个延时,在延时结束有才会进行操作(比如页面的跳转),在此期间,我们可以去加载数据,之后的跳转会是一个平滑的过渡,以此可以提升用户体验。
关键API:useTransition,他会返回一个startTransition函数和isPending状态。举个栗子:
function App() {
// 获取数据
const [resource, setResource] = useState(initialResource);
// 使用一个Transition,延时三秒
const [startTransition, isPending] = useTransition({
timeoutMs: 3000
});
return (
<>
<button
disabled={isPending}
onClick={() => {
// 点击之后会在三秒后才进行startTransition里的操作
startTransition(() => {
const nextUserId = getNextId(resource.userId);
setResource(fetchProfileData(nextUserId));
});
}}
>
Next
</button>
{isPending ? " Loading..." : null}
<ProfilePage resource={resource} />
</>
);
}