文档
Remix Hello World — 博客首页
目标
创建一个最简单的 Remix 博客首页,展示 loader 数据加载和页面渲染。
完整代码
路由文件 app/routes/_index.tsx
import { json } from "@remix-run/node";
import { useLoaderData, Link } from "@remix-run/react";
// 模拟数据
const posts = [
{ id: 1, title: "Remix 入门指南", author: "张三", date: "2024-01-15" },
{ id: 2, title: "为什么选择 Remix", author: "李四", date: "2024-02-20" },
{ id: 3, title: "Web 标准的力量", author: "王五", date: "2024-03-10" },
];
// loader 在服务端运行,为组件提供数据
export function loader() {
return json({ posts });
}
export default function Index() {
const { posts } = useLoaderData<typeof loader>();
return (
<main style={mainStyle}>
<h1>📝 我的博客</h1>
<p style={{ color: "#666" }}>基于 Remix 构建 — 拥抱 Web 标准</p>
<ul style={{ listStyle: "none", padding: 0 }}>
{posts.map((post) => (
<li key={post.id} style={cardStyle}>
<Link to={`/post/${post.id}`} style={linkStyle}>
{post.title}
</Link>
<div style={{ fontSize: "0.85rem", color: "#888", marginTop: 4 }}>
{post.author} · {post.date}
</div>
</li>
))}
</ul>
</main>
);
}
const mainStyle: React.CSSProperties = {
maxWidth: 640, margin: "60px auto", padding: "0 16px",
fontFamily: "-apple-system, 'Microsoft YaHei', sans-serif",
};
const cardStyle: React.CSSProperties = {
padding: "16px 0",
borderBottom: "1px solid #eee",
};
const linkStyle: React.CSSProperties = {
color: "#6366f1", textDecoration: "none",
fontSize: "1.1rem", fontWeight: 600,
};
根布局 app/root.tsx
import { Links, Meta, Outlet, Scripts, ScrollRestoration } from "@remix-run/react";
export default function App() {
return (
<html lang="zh-CN">
<head>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<Meta />
<Links />
</head>
<body>
<Outlet />
<ScrollRestoration />
<Scripts />
</body>
</html>
);
}
文章详情页 app/routes/post.$postId.tsx
import { json } from "@remix-run/node";
import { useLoaderData, Link } from "@remix-run/react";
const posts = {
"1": { title: "Remix 入门指南", content: "Remix 是基于 Web 标准的全栈框架...", author: "张三" },
"2": { title: "为什么选择 Remix", content: "嵌套路由、数据加载、表单处理...", author: "李四" },
"3": { title: "Web 标准的力量", content: "Fetch API、FormData、Response...", author: "王五" },
};
export function loader({ params }: { params: { postId: string } }) {
const post = posts[params.postId as keyof typeof posts];
if (!post) throw new Response("Not Found", { status: 404 });
return json({ post });
}
export default function Post() {
const { post } = useLoaderData<typeof loader>();
return (
<main style={{ maxWidth: 640, margin: "60px auto", padding: "0 16px" }}>
<Link to="/" style={{ color: "#6366f1" }}>← 返回首页</Link>
<h1>{post.title}</h1>
<p>{post.content}</p>
<p style={{ color: "#888" }}>作者:{post.author}</p>
</main>
);
}
运行步骤
npm run dev
# 访问 http://localhost:3000
预期输出
- 首页显示 3 篇文章列表
- 点击文章标题进入详情页
- 详情页有返回链接
- 所有页面均为服务端渲染(查看源代码可见 HTML)