Remix路由复用问题:如何复用路由Loader与组件避免重复维护
Remix 路由Loader与组件复用解决方案
先解决404问题
你遇到的404是因为管理页的文章链接路径不对——当前链接应该还是指向/posts/${slug},但你的管理路由结构是posts.admin作为父路由,对应的文章详情路由应该是posts.admin.$slug,所以需要把链接路径改成/posts/admin/${slug}才能匹配到正确的路由。
复用Loader与组件的具体实现
1. 抽离核心Loader逻辑到公共文件
把获取文章的核心逻辑抽离到服务端工具文件,避免在多个路由里重复写:
// app/utils/posts.server.ts // 假设你用Prisma或其他ORM操作数据库,这里替换成你的实际查询逻辑 import { db } from "./db.server"; // 基础获取文章方法 export async function getPostBySlug(slug: string) { const post = await db.post.findUnique({ where: { slug }, include: { author: true } // 按需关联查询 }); if (!post) { throw new Response("文章不存在", { status: 404 }); } return post; } // 可选:管理端专属的文章查询(带权限校验) export async function getPostBySlugForAdmin(slug: string, userId: string) { // 先校验用户权限 const user = await db.user.findUnique({ where: { id: userId } }); if (!user?.isAdmin) { throw new Response("无权限访问", { status: 403 }); } return getPostBySlug(slug); }
2. 抽离文章展示组件
把文章内容展示的UI部分单独抽成组件,供普通页和管理页复用:
// app/components/PostDisplay.tsx type PostDisplayProps = { post: { slug: string; title: string; content: string; author: { name: string }; }; }; export default function PostDisplay({ post }: PostDisplayProps) { return ( <article className="post-content"> <h1>{post.title}</h1> <p className="author">作者:{post.author.name}</p> <div dangerouslySetInnerHTML={{ __html: post.content }} /> </article> ); }
3. 在普通路由中复用
// app/routes/posts.$slug.tsx import { LoaderFunctionArgs } from "@remix-run/node"; import { useLoaderData } from "@remix-run/react"; import { getPostBySlug } from "~/utils/posts.server"; import PostDisplay from "~/components/PostDisplay"; export async function loader({ params }: LoaderFunctionArgs) { const slug = params.slug; if (!slug) throw new Response("参数错误", { status: 400 }); return getPostBySlug(slug); } export default function PostPage() { const post = useLoaderData<typeof loader>(); return <PostDisplay post={post} />; }
4. 在管理路由中复用
// app/routes/posts.admin.$slug.tsx import { LoaderFunctionArgs } from "@remix-run/node"; import { useLoaderData } from "@remix-run/react"; import { getPostBySlugForAdmin } from "~/utils/posts.server"; import PostDisplay from "~/components/PostDisplay"; import { getUserIdFromSession } from "~/utils/auth.server"; // 你的会话用户ID获取方法 export async function loader({ params, request }: LoaderFunctionArgs) { const slug = params.slug; if (!slug) throw new Response("参数错误", { status: 400 }); const userId = await getUserIdFromSession(request); return getPostBySlugForAdmin(slug, userId); } export default function AdminPostPage() { const post = useLoaderData<typeof loader>(); return ( <div className="admin-post-page"> {/* 这里可以添加管理操作按钮,比如编辑、删除 */} <button>编辑文章</button> <button>删除文章</button> <PostDisplay post={post} /> </div> ); }
5. 确保父路由结构正确
你的posts.admin.tsx父路由要包含<Outlet/>,让子路由内容能渲染:
// app/routes/posts.admin.tsx import { Outlet, Link } from "@remix-run/react"; export default function PostAdmin() { return ( <div className="admin-layout"> <nav> <Link to="/posts/admin">管理首页</Link> </nav> <Outlet /> </div> ); }
这样既解决了404问题,又实现了Loader逻辑和展示组件的复用,避免了重复维护两份代码。
内容的提问来源于stack exchange,提问作者Jason Rogers
相关产品推荐
相关产品推荐

