页面布局
本指南探讨了_页面布局_的抽象 — 当多个页面共享相同的整体结构,仅在主要内容上有所不同时。
最简单的布局可以在此页面上看到。它有一个带有站点导航的头部、两个侧边栏和一个带有外部链接的页脚。没有复杂的业务逻辑,唯一的动态部分是侧边栏和头部右侧的切换器。这样的布局可以完全放置在 shared/ui 或 app/layouts 中,通过 props 填充侧边栏的内容:
import { Link, Outlet } from "react-router-dom";import { useThemeSwitcher } from "./useThemeSwitcher";
export function Layout({ siblingPages, headings }) { const [theme, toggleTheme] = useThemeSwitcher();
return ( <div> <header> <nav> <ul> <li> <Link to="/">Home</Link> </li> <li> <Link to="/docs">Docs</Link> </li> <li> <Link to="/blog">Blog</Link> </li> </ul> </nav> <button onClick={toggleTheme}>{theme}</button> </header> <main> <SiblingPageSidebar siblingPages={siblingPages} /> <Outlet /> {/* 这里是主要内容的位置 */} <HeadingsSidebar headings={headings} /> </main> <footer> <ul> <li>GitHub</li> <li>Twitter</li> </ul> </footer> </div> );}export function useThemeSwitcher() { const [theme, setTheme] = useState("light");
function toggleTheme() { setTheme(theme === "light" ? "dark" : "light"); }
useEffect(() => { document.body.classList.remove("light", "dark"); document.body.classList.add(theme); }, [theme]);
return [theme, toggleTheme] as const;}侧边栏的代码留给读者作为练习 😉。
在布局中使用 widgets
Section titled “在布局中使用 widgets”有时您希望在布局中包含某些业务逻辑,特别是如果您使用像 React Router 这样的路由器的深度嵌套路由。然后由于层上的导入规则,您无法将布局存储在 Shared 或 Widgets 中:
slice 中的模块只能在其他 slices 位于严格较低的层时导入它们。
在我们讨论解决方案之前,我们需要讨论这是否首先是一个问题。您_真的需要_那个布局吗?如果需要,它_真的需要_成为一个 Widget 吗?如果所讨论的业务逻辑块在 2-3 个页面上重用,而布局只是该 widget 的一个小包装器,请考虑以下两个选项之一:
-
在 App 层内联编写布局,在那里配置路由
这对于支持嵌套的路由器来说很棒,因为您可以将某些路由分组并仅对它们应用布局。 -
直接复制粘贴
抽象代码的冲动往往被过度高估。对于很少更改的布局来说尤其如此。在某个时候,如果其中一个页面需要更改,您可以简单地进行更改,而不会不必要地影响其他页面。如果您担心有人可能忘记更新其他页面,您总是可以留下描述页面之间关系的注释。
如果上述都不适用,有两种解决方案可以在布局中包含 widget:
- 使用 render props 或 slots
大多数框架允许您从外部传递一段 UI。在 React 中,这被称为 render props,在 Vue 中被称为 slots。 - 将布局移动到 App 层
您也可以将布局存储在 App 层,例如在app/layouts中,并组合您想要的任何 widgets。
- 在教程中有一个如何使用 React 和 Remix(相当于 React Router)构建带有身份验证的布局的示例。