Layouts - 布局

什么是 layouts? 应用程序通常具有不同的 layouts: 例如,登录页面通常跟管理后台布局不同。 Layouts 是指给不同的页面定义不同的布局

简单方式

为每个页面导入并使用正确的 layout:

// /pages/index.page.js

export { Page };

import { LayoutDefault } from "../layouts/LayoutDefault";

function Page() {
  return (
    <>
      <LayoutDefault>...</LayoutDefault>
    </>
  );
}
// /pages/admin.page.js

export { Page };

import { LayoutDashboard } from "../layouts/LayoutDashboard";

function Page() {
  return (
    <>
      <LayoutDashboard>...</LayoutDashboard>
    </>
  );
}

自定义导出

或者,我们可以使用 Custom Export

// /pages/admin.page.js

// 将 `pageContext.exports.Layout` 设置为 `LayoutDashboard`
export { LayoutDashboard as Layout } from "../layouts/LayoutDashboard";
export { Page };

function Page() {
  // ...
}

然后在 renderer 中访问 pageContext.exports.Layout 并相应渲染它:

// /renderer/_default.page.{server|client}.js

export { render };

import { LayoutDefault } from "../layouts/LayoutDefault";

function render() {
  const Layout = pageContext.exports.Layout || LayoutDefault;
  const { Page } = pageContext;
  // render `<Layout><Page/></Layout>`
  // ...
}
// /pages/index.page.js

// 不需要 `export { Layout }` 因为落地页使用 `<LayoutDefault>`
export { Page };

function Page() {
  // As usual
  // ...
}

可使用 _default.page.js 一次性设置多个页面的 layout 布局:

// /pages/product/_default.page.js

// 设置所有 `/pages/product/**/*.page.js` 的layout
export { LayoutProduct as Layout } from "../../layouts/LayoutProduct";

这很适合 Domain-driven file structure

例子:

嵌套 Layouts 布局

什么是嵌套 layouts 嵌套 layout 布局本质上是一个页面有一个带有多个路由参数

比如 /product/@productId/@productView

URL                        productId     productView
/product/1337              1337          null
/product/1337/pricing      1337          pricing
/product/42/reviews        42            reviews
/product/42/pricing                   /product/42/reviews
+------------------+                  +-----------------+
| Product          |                  | Product         |
| +--------------+ |                  | +-------------+ |
| | Pricing      | |  +------------>  | | Reviews     | |
| |              | |                  | |             | |
| +--------------+ |                  | +-------------+ |
+------------------+                  +-----------------+

对于类似于 Next.js 的 Layout RFC, 请参考 #346。 如果你需要或想要这个功能,请添加评论,我们将实现它

如果嵌套 layout 布局不需要持久化或分配 URL(例如,“产品定价”页面和“产品评论”页面共享 URL /product/42),那么我们可以简单地使用有状态组件

#346 完成之前,实现嵌套 layout 布局的一种方法是使用路由函数:

// /pages/product/index.page.route.js

import { resolveRoute } from "vite-plugin-ssr/routing";

export default (pageContext) => {
  {
    const result = resolveRoute("/product/@id", pageContext.urlPathname);
    if (result.match) {
      result.routeParams.view = "overview";
      return result;
    }
  }
  {
    const result = resolveRoute(
      "/product/@id/reviews",
      pageContext.urlPathname
    );
    if (result.match) {
      result.routeParams.view = "reviews";
      return result;
    }
  }
  {
    const result = resolveRoute(
      "/product/@id/pricing",
      pageContext.urlPathname
    );
    if (result.match) {
      result.routeParams.view = "pricing";
      return result;
    }
  }
  return false;
};
// /pages/product/index.page.js

export { Page };

import { usePageContext } from "../../renderer/usePageContext";

function Page() {
  const pageContext = usePageContext();
  const { view } = pageContext.routeParams;
  const innerView =
    view === "overview" ? (
      <Overview />
    ) : view === "reviews" ? (
      <Reviews />
    ) : (
      <Pricing />
    );
  return (
    <>
      ...
      Somewhere deep
      {innerView}
      ...
    </>
  );
}

usePageContext() 使得所有组件都可访问 pageContext,请参阅 随处访问 pageContext

对于平滑的嵌套 layout 导航,我们建议使用 Client Routing

// pages/product/index.page.client.js

// 如果应用程序已经使用客户端路由,则可以省略
export const clientRouting = true;

使用 <a href="/product/42/reviews" keep-scroll-position /> (或 navigate('/product/42/reviews', { keepScrollPosition: true })) 来避免浏览器滚动到顶部

例子 (参考 /starship 页面):