其他
在开发 Remix + TypeScript 全栈应用时…
适用于使用 Remix 和 TypeScript 构建服务端渲染、表单驱动、可渐进增强的全栈 Web 应用。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 38/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 55/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 59/100 ×0.15
适用场景
在开发 Remix + TypeScri特定
解决思路
- 编写简洁、技术化的 Remix 代码,并提供准确的 TypeScript 示例。
- 遵循渐进增强和 Web 标准。
- 使用 loaders 进行数据获取,使用 actions 进行数据变更。
- 利用嵌套路由组织代码。
- 优先服务端渲染和 Web 基础能力。
- 按 app/ 组织项目:components/ 放可复用 React 组件,models/ 放数据库模型和类型,routes/ 放路由文件(如 _index.tsx 对应 /,about.tsx 对应 /about,posts/_index.tsx 对应 /posts,posts/$slug.tsx 对应 /posts/:slug),styles/ 放 CSS 文件,utils/ 放工具函数,entry.client.tsx 和 entry.server.tsx 分别是客户端和服务端入口,root.tsx 是根布局。
- Basic Loader:从 @remix-run/node 导入 LoaderFunctionArgs 和 json;导出 async loader({ params }: LoaderFunctionArgs),调用 getPost(params.slug);若 !post 则 throw new Response('Not Found', { status: 404 });否则 return json({ post });组件中用 useLoaderData<typeof loader>() 取数据并渲染。
- Loader with Authentication:从 @remix-run/node 导入 redirect,从 ~/utils/session.server 导入 getUser;导出 async loader({ request }: LoaderFunctionArgs),调用 await getUser(request);若 !user 则 throw redirect('/login');否则 return json({ user })。
- Form Handling:从 @remix-run/node 导入 json 和 redirect;导出 async action({ request }: ActionFunctionArgs),await request.formData() 读取 title 和 content;构造 errors 对象,若 !title 则 errors.title = 'Title is required';若 errors 非空则 return json({ errors }, { status: 400 });否则 await createPost({ title, content }) 并 return redirect(`/posts/${post.slug}`)。
- Using Action Data:从 @remix-run/react 导入 useActionData 和 Form;在组件中 const actionData = useActionData<typeof action>(); 使用 <Form method="post">,包含 name="title" 的 input 和 name="content" 的 textarea;若 actionData?.errors?.title 存在则渲染错误 <p className="error">;提交按钮文本 Create Post。
- Layout Routes:在 routes/dashboard.tsx 中从 @remix-run/react 导入 Outlet;导出默认 DashboardLayout,渲染 div.dashboard,内部 nav 含 Link to="/dashboard" 和 Link to="/dashboard/settings",main 中放 <Outlet />。
- Pathless Layouts:创建 routes/_auth.tsx 作为 /login、/register 的无路径布局;导出默认 AuthLayout,渲染 div.auth-container,内部放 <Outlet />。
- Non-Navigation Fetches:从 @remix-run/react 导入 useFetcher;在 LikeButton 组件中 const fetcher = useFetcher(); const isLiking = fetcher.state !== 'idle'; 使用 <fetcher.Form method="post" action="/api/like">,包含 hidden input name="postId" value={postId},按钮 disabled={isLiking},文本根据 isLiking 显示 Liking... 或 Like。
- Optimistic UI:从 @remix-run/react 导入 useFetcher;在 TodoItem 中 const fetcher = useFetcher(); const isDeleting = fetcher.formData?.get('_action') === 'delete'; 若 isDeleting 则 return null 以乐观移除;否则渲染 li,包含 todo.title 和 <fetcher.Form method="post">,hidden input name="_action" value="delete"、hidden input name="id" value={todo.id}、提交按钮 Delete。
- Error Boundaries:从 @remix-run/react 导入 useRouteError 和 isRouteErrorResponse;导出 ErrorBoundary 组件,调用 useRouteError();若 isRouteErrorResponse(error) 则渲染 error.status、error.statusText、error.data;否则渲染 Error 标题和 error instanceof Error ? error.message : 'Unknown error'。
- Resource Routes:在 routes/api/posts.tsx 中从 @remix-run/node 导入 json;导出 async loader(),调用 getPosts() 并 return json(posts);不提供 default export,使其成为无 UI 的 resource route。
- Meta and Links:从 @remix-run/node 导入 MetaFunction 和 LinksFunction;导出 const meta: MetaFunction<typeof loader> = ({ data }) => 返回数组 [{ title: data?.post.title ?? 'Blog' }, { name: 'description', content: data?.post.excerpt }];导出 const links: LinksFunction = () => 返回数组 [{ rel: 'stylesheet', href: styles }, { rel: 'preconnect', href: 'https://fonts.googleapis.com' }]。
- Session Management:在 utils/session.server.ts 中从 @remix-run/node 导入 createCookieSessionStorage 和 redirect;调用 createCookieSessionStorage 配置 cookie:name: '__session'、httpOnly: true、path: '/'、sameSite: 'lax'、secrets: [process.env.SESSION_SECRET!]、secure: process.env.NODE_ENV === 'production'。
- 创建 createUserSession(userId: string, redirectTo: string):await sessionStorage.getSession(),session.set('userId', userId),return redirect(redirectTo, { headers: { 'Set-Cookie': await sessionStorage.commitSession(session) } })。
- 使用 <Link prefetch="intent"> 预取。
- 使用 defer 进行流式数据加载。
- 通过 headers 实现 stale-while-revalidate。
- 通过 dynamic imports 做代码分割。
- 适当缓存 loader 响应。
- 始终在服务端验证表单数据。
- 使用 TypeScript 保证类型安全。
- 处理加载和错误状态。
- 实现适当的 CSRF 保护。
- 使用渐进增强。
- 在禁用 JavaScript 的情况下测试。
适用边界 · 注意事项
- 不使用 Remix 或不需要服务端渲染/渐进增强的纯客户端 SPA 项目
- 已采用其他全栈框架且不想引入 Remix 的路由与数据约定
- 需要大量实时双向通信而非请求/表单驱动交互的场景