Remix框架下如何自动生成所有文章路由链接?
Remix博客自动生成文章列表的方案
你可以不用自己从零写扫描脚本,有几个开箱即用的方式实现自动生成文章列表:
1. 在Loader中动态扫描MDX文件
直接在首页路由的loader函数里,用Node.js的glob包扫描路由目录下的MDX文件,提取元数据和路由路径:
// app/routes/_index.tsx import { glob } from "glob"; import { useLoaderData } from "@remix-run/react"; import type { LoaderFunctionArgs } from "@remix-run/node"; export async function loader({}: LoaderFunctionArgs) { // 扫描routes下的MDX文件,排除首页和非文章类路由文件 const files = await glob("app/routes/**/*.mdx", { ignore: ["app/routes/_index.tsx", "app/routes/*.tsx"] }); const posts = await Promise.all( files.map(async (filePath) => { // 转换文件路径为路由路径,比如app/routes/posts/hello.mdx -> /posts/hello const routePath = filePath .replace("app/routes", "") .replace(".mdx", "") .replace(/\/index$/, ""); // 动态导入MDX文件获取元数据 const postModule = await import(`../${filePath.slice(4)}`); return { path: routePath || "/", meta: postModule.meta || {}, }; }) ); // 按元数据中的创建日期倒序排列 const sortedPosts = posts.sort((a, b) => new Date(b.meta.createdAt).getTime() - new Date(a.meta.createdAt).getTime() ); return { posts: sortedPosts }; } export default function Index() { const { posts } = useLoaderData<typeof loader>(); return ( <div className="blog-index"> <h1>我的博客</h1> <ul className="post-list"> {posts.map((post) => ( <li key={post.path} className="post-item"> <a href={post.path}>{post.meta.title}</a> <p className="post-excerpt">{post.meta.description}</p> <small className="post-date"> {new Date(post.meta.createdAt).toLocaleDateString()} </small> </li> ))} </ul> </div> ); }
注意:需要先安装依赖包npm install glob @types/glob,并确保tsconfig.json允许动态导入MDX文件。
2. 构建时生成静态目录文件(你提到的方案)
如果偏好构建时生成静态文件,可在package.json中添加前置脚本:
{ "scripts": { "generate-toc": "node scripts/generate-toc.js", "build": "npm run generate-toc && remix build", "dev": "npm run generate-toc && remix dev" } }
脚本示例(scripts/generate-toc.js):
const fs = require("fs"); const path = require("path"); const glob = require("glob"); async function generateToc() { const files = await glob("app/routes/**/*.mdx", { ignore: ["app/routes/_index.tsx"] }); const posts = await Promise.all( files.map(async (file) => { const module = await import(path.resolve(__dirname, "..", file)); const routePath = file .replace("app/routes", "") .replace(".mdx", "") .replace(/\/index$/, ""); return { path: routePath, ...module.meta }; }) ); const sortedPosts = posts.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime() ); const content = `export const posts = ${JSON.stringify(sortedPosts, null, 2)} as const;`; fs.writeFileSync(path.resolve(__dirname, "..", "app/data/TableOfContents.ts"), content); } generateToc().catch(console.error);
之后在首页路由中直接导入posts变量使用即可。
关于文档提到的扩展性
正如你所说,文章数量不多时,每次修改重新部署完全不是问题,上述方案都能满足“不用手动添加链接”的核心需求,直到文章规模达到需要数据库存储的程度。
内容的提问来源于stack exchange,提问作者dwjohnston
相关产品推荐
相关产品推荐

