手机介绍网站wordpress新闻发布

张小明 2026/1/1 13:12:38
手机介绍网站,wordpress新闻发布,盘锦企业网站建设,网站建设合同印花税税率大家好#xff0c;我是jobleap.cn的小九。 SWR#xff08;Stale-While-Revalidate#xff09;是 Vercel 推出的轻量级数据请求库#xff0c;核心思想是“先返回缓存数据#xff08;Stale#xff09;#xff0c;再异步请求最新数据#xff08;Revalidate#xff09;”我是jobleap.cn的小九。SWRStale-While-Revalidate是 Vercel 推出的轻量级数据请求库核心思想是“先返回缓存数据Stale再异步请求最新数据Revalidate”特别适配 Next.js 服务端/客户端渲染场景。本文基于 Next.js 14App Router全面讲解 SWR 核心 API 用法并通过实战案例串联所有常用功能。一、环境准备1. 创建 Next.js 项目# 创建项目选择 App Router、TypeScript 可选npx create-next-applatest swr-tutorialcdswr-tutorial2. 安装 SWRnpminstallswr# 或 yarn add swr / pnpm add swr二、核心 API 详解与实战1. 基础useSWR核心钩子useSWR是 SWR 的核心接收 3 个参数key请求标识、fetcher请求函数、options配置项返回数据、加载状态、错误等核心状态。示例获取单条用户数据// app/user/[id]/page.tsx use client; // App Router 中客户端组件需声明 import useSWR from swr; // 1. 定义请求函数fetcher const fetcher async (url: string) { const res await fetch(url); if (!res.ok) throw new Error(请求失败); return res.json(); }; export default function UserPage({ params }: { params: { id: string } }) { const { id } params; // 2. 使用 useSWR 发起请求 const { data: user, isLoading, error } useSWR( /api/users/${id}, // key请求标识为空则不发起请求 fetcher, // 请求函数 { revalidateOnFocus: true, // 页面聚焦时重新验证默认开启 dedupingInterval: 5000, // 5 秒内避免重复请求默认 2000 retry: 2, // 请求失败重试次数默认 3 } ); // 加载中状态 if (isLoading) return div加载中.../div; // 错误处理 if (error) return div错误{error.message}/div; // 渲染数据 return ( div h1用户详情/h1 pID{user.id}/p p名称{user.name}/p p邮箱{user.email}/p /div ); }配套 API 接口模拟数据// app/api/users/[id]/route.ts export async function GET( req: Request, { params }: { params: { id: string } } ) { // 模拟数据库数据 const users [ { id: 1, name: 张三, email: zhangsanexample.com }, { id: 2, name: 李四, email: lisiexample.com }, ]; const user users.find(u u.id params.id); if (!user) return new Response(用户不存在, { status: 404 }); return Response.json(user); }2. 数据修改mutate本地更新 重新验证mutate用于手动更新 SWR 缓存数据支持“本地更新乐观 UI 异步重新验证”是实现数据修改的核心 API。示例编辑用户信息乐观更新// app/user/[id]/page.tsx续上 import { useState } from react; import { mutate } from swr; // 导入 mutate export default function UserPage({ params }: { params: { id: string } }) { const { id } params; const [name, setName] useState(); const { data: user, isLoading, error } useSWR(/api/users/${id}, fetcher); // 加载/错误处理逻辑同上... // 3. 编辑用户名称 const handleUpdateName async () { if (!name) return; // 乐观更新先修改缓存立即更新 UI mutate( /api/users/${id}, // 要更新的缓存 key { ...user, name }, // 新的缓存数据 false // 暂时不自动重新验证后续手动触发 ); try { // 发送修改请求到后端 await fetch(/api/users/${id}, { method: PATCH, headers: { Content-Type: application/json }, body: JSON.stringify({ name }), }); // 手动重新验证拉取最新数据可选 mutate(/api/users/${id}); } catch (err) { // 出错回滚恢复原缓存数据 mutate(/api/users/${id}, user, false); alert(修改失败); } }; return ( div {/* 原有渲染逻辑 */} div input typetext placeholder修改名称 value{name} onChange{(e) setName(e.target.value)} defaultValue{user?.name} / button onClick{handleUpdateName}保存/button /div /div ); }配套修改接口// app/api/users/[id]/route.ts新增 PATCH 方法 export async function PATCH( req: Request, { params }: { params: { id: string } } ) { const { name } await req.json(); // 模拟更新数据库 return Response.json({ id: params.id, name, email: ${name}example.com }); }3. 无限加载useSWRInfiniteuseSWRInfinite专为分页/无限加载场景设计自动管理多个分页的缓存 key支持“加载更多”“上拉加载”等交互。示例博客文章无限加载// app/posts/page.tsx use client; import useSWRInfinite from swr/infinite; import { useState } from react; const fetcher async (url: string) { const res await fetch(url); if (!res.ok) throw new Error(请求失败); return res.json(); }; export default function PostsPage() { // 分页页码默认从 0 开始 const [pageIndex, setPageIndex] useState(0); // 4. 使用 useSWRInfinite 实现无限加载 const { data, isLoading, error, size, setSize } useSWRInfinite( // 生成分页 key第 1 个参数是 pageIndex第 2 个是 previousPageData (pageIndex, previousPageData) { // 如果上一页没有数据停止加载 if (previousPageData !previousPageData.posts.length) return null; // 分页请求 URL return /api/posts?page${pageIndex 1}limit5; }, fetcher, { revalidateAll: false, // 加载更多时不重新验证所有页默认 false persistSize: true, // 保留分页大小默认 true } ); // 格式化数据合并所有分页数据 const posts data ? data.flatMap((page) page.posts) : []; // 判断是否还有更多数据 const isLoadingMore isLoading; const hasMore data ? data[data.length - 1]?.posts.length 5 : true; // 加载中/错误处理 if (isLoading size 1) return div加载中.../div; if (error) return div错误{error.message}/div; // 加载更多按钮点击事件 const loadMore () setSize(size 1); return ( div h1博客文章列表/h1 div {posts.map((post) ( div key{post.id} style{{ margin: 10px 0, padding: 10px, border: 1px solid #eee }} h3{post.title}/h3 p{post.content}/p /div ))} /div button onClick{loadMore} disabled{!hasMore || isLoadingMore} style{{ marginTop: 20px }} {isLoadingMore ? 加载中... : hasMore ? 加载更多 : 没有更多了} /button /div ); }配套分页接口// app/api/posts/route.ts export async function GET(req: Request) { const { searchParams } new URL(req.url); const page Number(searchParams.get(page)) || 1; const limit Number(searchParams.get(limit)) || 5; // 模拟分页数据 const totalPosts 12; // 总共有 12 篇文章 const posts Array.from({ length: Math.min(limit, totalPosts - (page - 1) * limit) }) .map((_, index) ({ id: (page - 1) * limit index 1, title: 文章 ${(page - 1) * limit index 1}, content: 这是第 ${page} 页的第 ${index 1} 篇文章, })); return Response.json({ posts, total: totalPosts }); }4. 依赖请求条件/链式请求SWR 支持通过key控制请求触发时机实现“先请求 A再请求 B”的链式依赖。示例先获取当前用户 ID再获取其文章// app/profile/posts/page.tsx use client; import useSWR from swr; const fetcher async (url: string) { const res await fetch(url); if (!res.ok) throw new Error(请求失败); return res.json(); }; export default function ProfilePostsPage() { // 第一步获取当前用户信息含 ID const { data: currentUser } useSWR(/api/current-user, fetcher); // 第二步依赖用户 ID获取其文章key 为空则不发起请求 const { data: userPosts, isLoading, error } useSWR( currentUser ? /api/users/${currentUser.id}/posts : null, // 条件 key fetcher ); if (isLoading) return div加载中.../div; if (error) return div错误{error.message}/div; return ( div h1{currentUser?.name} 的文章/h1 {userPosts?.length 0 ? ( p暂无文章/p ) : ( userPosts?.map((post) ( div key{post.id}{post.title}/div )) )} /div ); }5. 批量请求useSWRsuseSWRs用于批量请求多个独立的 key返回多个 SWR 响应结果适合一次性加载多个不相关的数据。示例批量加载多个分类的文章数// app/categories/page.tsx use client; import useSWRs from swr/批量; // 实际导入import useSWRs from swr import useSWR from swr; const fetcher async (url: string) { const res await fetch(url); if (!res.ok) throw new Error(请求失败); return res.json(); }; export default function CategoriesPage() { // 要批量请求的分类 ID 列表 const categoryIds [1, 2, 3]; // 5. 使用 useSWRs 批量请求 const results useSWRs( categoryIds.map((id) /api/categories/${id}/count), // 批量 key 数组 fetcher ); // 合并加载/错误状态 const isLoading results.some((r) r.isLoading); const error results.find((r) r.error)?.error; if (isLoading) return div加载中.../div; if (error) return div错误{error.message}/div; return ( div h1各分类文章数/h1 {categoryIds.map((id, index) ( div key{id} 分类 {id}{results[index].data.count} 篇文章 /div ))} /div ); }6. 高级配置缓存控制与重新验证SWR 提供丰富的配置项控制缓存和重新验证行为核心配置如下配置项作用默认值revalidateOnFocus页面聚焦时重新验证truerevalidateOnReconnect网络重连时重新验证truededupingInterval避免重复请求的时间窗口2000msrevalidateIfStale缓存过期后是否自动重新验证truestaleWhileRevalidate缓存“新鲜期”过期后仍返回缓存并异步更新undefinedonError全局错误回调undefined示例全局配置 SWR可通过SWRConfig组件为整个应用配置默认选项// app/layout.tsx use client; // 注意App Router 根布局需客户端组件才能使用 SWRConfig import { SWRConfig } from swr; const fetcher async (url: string) { const res await fetch(url); if (!res.ok) throw new Error(请求失败); return res.json(); }; export default function RootLayout({ children, }: { children: React.ReactNode; }) { return ( html langzh-CN body SWRConfig value{{ fetcher, // 全局默认 fetcher revalidateOnFocus: false, // 关闭页面聚焦重新验证 dedupingInterval: 10000, // 10 秒内避免重复请求 onError: (err) { console.error(SWR 请求错误, err); // 可集成错误监控工具如 Sentry }, }} {children} /SWRConfig /body /html ); }三、综合实战串联所有 API 的博客系统以下是整合所有核心 API 的完整示例包含“用户信息展示 文章无限加载 文章编辑 批量分类统计”// app/dashboard/page.tsx use client; import useSWR from swr; import useSWRInfinite from swr/infinite; import { useState } from react; import { mutate } from swr; // 全局 fetcher const fetcher async (url: string) { const res await fetch(url); if (!res.ok) throw new Error(请求失败); return res.json(); }; export default function DashboardPage() { // 1. 获取当前用户基础 useSWR const { data: currentUser } useSWR(/api/current-user, fetcher); // 2. 无限加载用户文章useSWRInfinite const { data: postPages, size, setSize, isLoading: isLoadingPosts } useSWRInfinite( (pageIndex) currentUser ? /api/users/${currentUser.id}/posts?page${pageIndex 1}limit3 : null, fetcher ); const posts postPages ? postPages.flatMap((page) page.posts) : []; const hasMore postPages ? postPages[postPages.length - 1]?.posts.length 3 : true; // 3. 批量获取分类统计useSWRs const categoryIds [1, 2, 3]; const categoryResults useSWR( currentUser ? categoryIds.map((id) /api/categories/${id}/count) : null, (urls) Promise.all(urls.map(fetcher)) // 手动实现批量请求等价 useSWRs ); // 4. 编辑文章mutate 乐观更新 const [editingPostId, setEditingPostId] useStatestring | null(null); const [newTitle, setNewTitle] useState(); const handleEditPost async (postId: string) { if (!newTitle) return; const post posts.find((p) p.id postId); if (!post) return; // 乐观更新缓存 mutate( /api/users/${currentUser.id}/posts, (prevPages: any[]) prevPages.map((page) ({ ...page, posts: page.posts.map((p) p.id postId ? { ...p, title: newTitle } : p) })), false ); try { await fetch(/api/posts/${postId}, { method: PATCH, body: JSON.stringify({ title: newTitle }), headers: { Content-Type: application/json }, }); mutate(/api/users/${currentUser.id}/posts); // 重新验证 setEditingPostId(null); } catch (err) { mutate(/api/users/${currentUser.id}/posts); // 回滚缓存 alert(编辑失败); } }; // 加载状态处理 if (!currentUser || isLoadingPosts) return div加载中.../div; return ( div style{{ padding: 20px }} h1欢迎{currentUser.name}/h1 {/* 分类统计 */} div style{{ margin: 20px 0 }} h3分类文章统计/h3 {categoryResults.data?.map((data, index) ( div key{categoryIds[index]} 分类 {categoryIds[index]}{data.count} 篇 /div ))} /div {/* 文章列表 */} h3我的文章/h3 {posts.length 0 ? ( p暂无文章/p ) : ( posts.map((post) ( div key{post.id} style{{ margin: 10px 0, padding: 10px, border: 1px solid #eee }} {editingPostId post.id ? ( div input typetext value{newTitle} onChange{(e) setNewTitle(e.target.value)} defaultValue{post.title} / button onClick{() handleEditPost(post.id)}保存/button button onClick{() setEditingPostId(null)}取消/button /div ) : ( div h4{post.title}/h4 button onClick{() { setEditingPostId(post.id); setNewTitle(post.title); }}编辑/button /div )} /div )) )} {/* 加载更多 */} button onClick{() setSize(size 1)} disabled{!hasMore || isLoadingPosts} style{{ marginTop: 10px }} {isLoadingPosts ? 加载中... : hasMore ? 加载更多 : 没有更多} /button /div ); }四、总结SWR 凭借“缓存优先 异步更新”的核心设计成为 Next.js 项目中数据请求的首选方案。本文覆盖了useSWR基础请求与错误处理mutate实现乐观 UI 更新useSWRInfinite无限加载分页数据条件/链式依赖请求批量请求与全局配置核心缓存/重新验证配置。SWR 的优势在于自动缓存管理减少重复请求无缝适配 Next.js 客户端/服务端渲染轻量级仅 5KBAPI 简洁支持乐观更新、无限加载等高频交互场景。实际开发中可结合 SWR 与 Next.js 的 Server Components 分工Server Components 处理首屏数据渲染SWR 处理客户端动态数据更新兼顾性能与交互体验。
版权声明:本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!

seo在网站制作杭州公司网站制作

paperxie-AI官网免费论文查重复率AIGC检测/开题报告/文献综述/论文初稿 https://www.paperxie.cn/ai/openingReporthttps://www.paperxie.cn/ai/openingReport 对刚接触学术写作的人来说,开题报告的 “难” 从来不是没内容可写,而是不知道 “怎么写才对…

张小明 2025/12/31 16:56:33 网站建设

浙江综合网站建设配件河南能源企业网站建设

HTTP定时任务自动化框架3步精通:从零基础到实战高手 【免费下载链接】qd QD [v20230821] —— HTTP请求定时任务自动执行框架 base on HAR Editor and Tornado Server 项目地址: https://gitcode.com/gh_mirrors/qd/qd 你是否曾被重复的HTTP请求操作困扰&…

张小明 2025/12/31 16:56:34 网站建设

萍乡网站建设哪家好好的建站平台

第一章:车路协同 Agent 的通信协议在车路协同系统(V2X, Vehicle-to-Everything)中,智能体(Agent)之间的高效、可靠通信是实现交通智能化的核心。这些智能体包括车载单元(OBU)、路侧单…

张小明 2025/12/31 16:56:33 网站建设

asp.net mvc5网站开发之美wordpress侧边栏html

模型的可靠性,归根结底源于其训练数据的质量、深度和清洁度。GPT-5.2 能够实现低幻觉率和高推理能力,离不开 OpenAI 在其训练数据和架构上所做的巨大投入。本篇将作为一份深度报告,详细解析GPT-5.2的训练数据构成、如何实现跨模态的“原生感知…

张小明 2025/12/31 16:56:32 网站建设

网站内容建设机制信息化建设官方网站

一、摘要微店店铺所有商品 API 是微店开放平台提供的核心数据接口,你可以通过该接口以编程方式获取指定微店店铺下的全部商品信息(如商品 ID、名称、售价、库存、主图、上下架状态等)。该接口主要用于店铺商品数据同步、批量商品管理、第三方…

张小明 2025/12/31 16:56:34 网站建设

网站建设工具品牌手机app界面设计图

ExplorerPatcher终极指南:从新手到高手的完整成长路径 【免费下载链接】ExplorerPatcher 提升Windows操作系统下的工作环境 项目地址: https://gitcode.com/GitHub_Trending/ex/ExplorerPatcher 还在为Windows 11 24H2的界面限制而烦恼?想要个性化…

张小明 2025/12/31 16:56:35 网站建设