先看总图:
用户访问 /ships/9527/edit
│
▼
┌──────────────────────────────────────┐
│ app/root.tsx │ 港务总控楼
│ ├─ Meta / Links / Scripts │ 文档外壳
│ └─ Outlet │ 下级港区入口
└──────────────────────────────────────┘
│
▼
┌──────────────────────────────────────┐
│ app/routes/ships.tsx │ 船舶港区
│ ├─ loader() │ 拉港区公共数据
│ └─ <Outlet /> │ 子泊位入口
└──────────────────────────────────────┘
│
▼
┌──────────────────────────────────────┐
│ app/routes/ships.$shipId.edit.tsx │ 9527 编辑泊位
│ ├─ loader() │ 读取船舶详情
│ ├─ action() │ 提交编辑表单
│ ├─ default component │ 渲染编辑界面
│ └─ ErrorBoundary │ 事故隔离
└──────────────────────────────────────┘
│
▼
浏览器收到 HTML + 数据 + 资源
│
▼
用户提交 <Form method="post">
│
▼
action() 写库
│
├─ redirect("/ships/9527")
└─ 或留在当前页并自动 revalidate loaders这张图里最关键的一点是:
路由模块不是只负责“显示哪个组件”,它同时定义了这个泊位的数据读取、数据写入、错误处理、元信息、样式依赖。
所以 Remix 不是“组件中心”思维,而是更像:
一个 URL 对应一个完整的业务泊位。