react 的并发特性让应用更加流畅和响应迅速。这些特性包括 useTransition、useDeferredValue、Suspense 和 useoptimistic。它们协同工作,能够显著提升用户体验。下面通过实际示例和最佳实践来了解这些功能。
并发渲染是 React 其他并发特性的基础。在并发渲染之前,React 采用同步更新方式。当触发更新时,React 会阻塞主线程,直到整个组件树重新渲染完成。这可能导致界面卡顿,特别是在处理复杂组件时,用户输入会变得迟钝。
并发渲染引入了可中断的渲染机制。React 可以开始渲染更新,然后暂停去处理更紧急的任务(比如用户输入),之后再从中断的地方继续渲染。这样就不会阻塞用户操作。
useTransition 可以将状态更新标记为非紧急任务,让 React 能够中断这些更新去处理更重要的用户操作。
const [isPending, startTransition] = useTransition();useTransition 不接收参数,返回一个数组。数组包含 isPending(表示是否有过渡任务在进行)和 startTransition(用于标记非紧急更新的函数)。
当有复杂计算可能阻塞用户交互时,可以使用 useTransition 来降低优先级。
标签切换组件示例:
function TabButton({ children, onTabChange }) {
const [isPending, startTransition] = useTransition();
const handleClick = () => {
startTransition(() => {
onTabChange();
});
};
return (
<button
onClick={handleClick}
style={{
opacity: isPending ? 0.6 : 1,
transition: 'opacity 0.2s'
}}
>
{children}
</button>
);
}对于 api 调用等异步操作,useTransition 可以协调状态更新,避免界面闪烁。
删除按钮示例:
function DeleteButton({ itemId, onDelete }) {
const [isPending, startTransition] = useTransition();
const handleDelete = async () => {
startTransition(async () => {
await onDelete(itemId);
});
};
return (
<button
onClick={handleDelete}
disabled={isPending}
style={{
opacity: isPending ? 0.6 : 1
}}
>
{isPending ? '删除中...' : '删除'}
</button>
);
}使用异步函数时,isPending 状态会自动管理,不需要手动设置加载状态。
Suspense 提供了声明式的加载边界,可以与 React.lazy 配合实现代码分割,也可以用于异步数据加载。
<Suspense fallback={<LoadingComponent />}>
<AsyncComponent />
</Suspense>Suspense 接收 fallback 属性(加载期间显示的组件)和可能异步加载的子组件。
配合 React.lazy 实现组件级代码分割:
const HeavyComponent = React.lazy(() => import('./HeavyComponent'));
function App() {
return (
<Suspense fallback={<div>加载中...</div>}>
<HeavyComponent />
</Suspense>
);
}为不同数据源设置独立的 Suspense 边界:
function UserDashboard({ userId }) {
return (
<div>
<h1>用户面板</h1>
<Suspense fallback={<div>加载用户信息...</div>}>
<UserProfile userId={userId} />
</Suspense>
<Suspense fallback={<div>加载文章列表...</div>}>
<UserPosts userId={userId} />
</Suspense>
</div>
);
}每个组件管理自己的数据,父组件通过 Suspense 统一处理加载状态。
将 useTransition 与 Suspense 结合,可以在导航时避免突兀的加载状态。
路由组件示例:
function AppRouter() {
const [currentPage, setCurrentPage] = useState('home');
const [isPending, startTransition] = useTransition();
function navigate(page) {
startTransition(() => {
setCurrentPage(page);
});
}
return (
<div>
<nav>
<button onClick={() => navigate('home')}>首页</button>
<button onClick={() => navigate('profile')}>个人资料</button>
<button onClick={() => navigate('settings')}>设置</button>
</nav>
<Suspense fallback={<div>页面加载中...</div>}>
<div style={{
opacity: isPending ? 0.7 : 1,
transition: 'opacity 0.2s'
}}>
<PageContent page={currentPage} />
</div>
</Suspense>
</div>
);
}在 Next.js App Router 中,导航会自动使用 useTransition。需要注意的是,useTransition 只会等待足够长的时间来避免隐藏已显示的内容,不会等待所有嵌套的 Suspense 边界。
use API 可以读取 Promise 和 Context 值,与 Suspense 配合良好。与 Hooks 不同,use 可以在条件语句中调用。
const data = use(promise);
const contextValue = use(Context);use 接收 Promise 或 Context,返回解析后的值。对于 Promise,会暂停组件直到解析完成。
function App() {
const userPromise = fetchUser('/api/user/123');
return (
<Suspense fallback={<div>加载用户信息...</div>}>
<UserProfile userPromise={userPromise} />
</Suspense>
);
}
function UserProfile({ userPromise }) {
const user = use(userPromise);
return (
<div>
<img src={user.avatar} alt={user.name} />
<h2>{user.name}</h2>
</div>
);
}use 会暂停 UserProfile 组件直到 Promise 解析完成,Suspense 在加载期间显示回退内容。
useDeferredValue 可以延迟更新依赖于频繁变化值的 UI 部分,在当前内容可见的同时准备新内容。
const deferredValue = useDeferredValue(value);useDeferredValue 接收一个值,返回一个延迟版本,在快速更新期间会暂时保持旧值。
当用户输入触发复杂计算时,使用 useDeferredValue 保持输入响应性:
function FilteredList({ items }) {
const [filter, setFilter] = useState('');
const deferredFilter = useDeferredValue(filter);
return (
<div>
<input
value={filter}
onChange={(e) => setFilter(e.target.value)}
placeholder="搜索..."
/>
<ExpensiveFilteredItems
items={items}
filter={deferredFilter}
/>
</div>
);
}
const ExpensiveFilteredItems = React.memo(function ({ items, filter }) {
const filteredItems = items.filter(item =>
item.name.toLowerCase().includes(filter.toLowerCase())
);
return (
<div>
{filteredItems.map(item => (
<div key={item.id}>{item.name}</div>
))}
</div>
);
});使用 React.memo 包装复杂组件,确保只在 props 实际变化时重新渲染。
结合 useDeferredValue 和 Suspense 实现搜索功能:
function SearchApp() {
const [query, setQuery] = useState('');
const deferredQuery = useDeferredValue(query);
const isStale = query !== deferredQuery;
return (
<div>
<input
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="搜索用户..."
/>
<div style={{
opacity: isStale ? 0.6 : 1,
transition: 'opacity 0.2s'
}}>
<Suspense fallback={<div>搜索中...</div>}>
<SearchResults query={deferredQuery} />
</Suspense>
</div>
</div>
);
}
function SearchResults({ query }) {
if (!query) {
return <div>请输入搜索关键词</div>;
}
const results = use(searchUsers(query));
return (
<div>
{results.map(user => (
<div key={user.id}>{user.name}</div>
))}
</div>
);
}通过比较当前查询词和延迟版本,使用透明度变化提示用户搜索结果正在更新。
useOptimistic 可以立即显示预期更新,而实际更新在后台进行。必须在 useTransition 中使用。
const [optimisticState, addOptimistic] = useOptimistic(
currentState,
updateFunction
);接收当前状态和更新函数,返回乐观状态和触发乐观更新的函数。
点赞按钮的乐观更新示例:
function LikeButton({ post, onToggleLike }) {
const [, startTransition] = useTransition();
const [optimisticPost, setOptimisticPost] = useOptimistic(
post,
(currentPost, shouldLike) => ({
...currentPost,
liked: shouldLike,
likes: currentPost.likes + (shouldLike ? 1 : -1)
})
);
const handleClick = () => {
startTransition(async () => {
setOptimisticPost(!optimisticPost.liked);
await onToggleLike(post.id, !optimisticPost.liked);
});
};
return (
<button onClick={handleClick}>
{optimisticPost.liked ? '是' : '否'} {optimisticPost.likes}
</button>
);
}点击时图标和计数立即变化,不需要额外的加载状态。
评论表单的乐观更新:
function CommentForm({ comments, onSubmit }) {
const [optimisticComments, addOptimisticComment] = useOptimistic(
comments,
(currentComments, newComment) => [
...currentComments,
{ ...newComment, isSubmitting: true }
]
);
const formRef = useRef();
const handleSubmit = async (formData) => {
const commentText = formData.get('comment');
addOptimisticComment({
id: Date.now(),
text: commentText
});
await onSubmit(commentText);
formRef.current?.reset();
};
return (
<div>
<form ref={formRef} action={handleSubmit}>
<textarea name="comment" required />
<button type="submit">发布评论</button>
</form>
<div>
{optimisticComments.map(comment => (
<div
key={comment.id}
style={{
opacity: comment.isSubmitting ? 0.7 : 1
}}
>
{comment.text}
{comment.isSubmitting && ' (发布中...)'}
</div>
))}
</div>
</div>
);
}提交表单时评论立即显示,带有发布中状态。请求完成后,乐观评论会被真实数据替换。如果请求失败,useOptimistic 会自动回滚。
React 的并发特性共同构建了更流畅的用户体验:
useTransition:创建低优先级的状态更新,在处理复杂任务和异步操作时保持界面响应
useDeferredValue:延迟渲染依赖频繁变化值的 UI 部分,防止界面卡顿
Suspense:为代码分割和异步操作提供声明式加载边界
useOptimistic:通过乐观更新让用户交互感觉更加即时
合理运用这些特性,可以显著提升 React 应用的性能和用户体验。在实际开发中,建议根据具体场景选择合适的并发特性,避免过度使用。
本文内容仅供个人学习/研究/参考使用,不构成任何决策建议或专业指导。分享/转载时请标明原文来源,同时请勿将内容用于商业售卖、虚假宣传等非学习用途哦~感谢您的理解与支持!
抢购、秒杀是平常很常见的场景,面试的时候面试官也经常会问到,比如问你淘宝中的抢购秒杀是怎么实现的等等。抢购、秒杀实现很简单,但是有些问题需要解决,主要针对两个问题:
最近在看Elasticsearch时看到了并发控制,由此看到了新的并发控制方式。不得不说Elasticsearch相较于关系型数据库就是两种理论建立的数据存储体系,当然它们在并发控制上也相差甚远,各有千秋。
在互联网时代,并发,高并发通常是指并发访问。也就是在某个时间点,有多少个访问同时到来。 高并发架构相关概念QPS (每秒查询率) : 每秒钟请求或者查询的数量,在互联网领域,指每秒响应请求数
今天看见有人聊目前系统有2亿的PV,该如何优化?当我看到这个话题的时候,突然在想自己工作中也遇到了不少高并发的场景了,所以即兴发挥,在这里简单总结和分享下,欢迎指正和补充。
很多网站有并发连接数的限制,所以当请求发送太快的时候会导致返回值为空或报错。 安装依赖 express superagent cheerio eventproxy。新建app.js 抓取所有的url
在秒杀,抢购等并发场景下,可能会出现超卖的现象,在PHP语言中并没有原生提供并发的解决方案,因此就需要借助其他方式来实现并发控制。列出常见的解决方案有:
并发编程三要素:原子性: 一个不可再被分割的颗粒。原子性指的是一个或多个操作要么全部执行成功要么全部执行失败。有序性: 程序执行的顺序按照代码的先后顺序执行。(处理器可能会对指令进行重排序)
异步是 js 一个非常重要的特性,但很多时候,我们不仅仅想让一系列任务并行执行,还想要控制同时执行的并发数,尤其是在针对操作有限资源的异步任务,比如文件句柄,网络端口等等。
Node可以在不新增额外线程的情况下,依然可以对任务进行并发处理 —— Node.js是单线程的。它通过事件循环(event loop)来实现并发操作,对此,我们应该要充分利用这一点 —— 尽可能的避免阻塞操作
在开发过程中,有时会遇到需要控制任务并发执行数量的需求。例如一个爬虫程序,可以通过限制其并发任务数量来降低请求频率,从而避免由于请求过于频繁被封禁问题的发生。
内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!