路由函数

路由函数为我们提供了编程灵活性来实现高级路由

// /pages/product/edit.page.route.js

import partRegex from "part-regex";

export default (pageContext) => {
  // 路由守卫
  if (!pageContext.user.isAdmin) {
    return false;
  }

  // 我们可以使用 RegExp 和任何我们想用的库
  if (!partRegex`/product/${/[0-9]+/}/edit`.test(pageContext.urlPathname)) {
    return false;
  }
  const id = pageContext.urlPathname.split("/")[2];

  return {
    // 把 `id` 传到 `pageContext.routeParams.id` 中
    routeParams: { id },
    // 路由优先级
    precedence: 10,
  };
};

precedence 数字用于解决路由冲突,详见:路由 > Routing 优先级

我们可以使用任何路由工具,例如:

resolveRoute()

路由函数可以使用 vite-plugin-ssr 内置的路由解析器

// /pages/product/edit.page.route.js

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

export default (pageContext) => {
  if (!pageContext.user.isAdmin) {
    return false;
  }
  return resolveRoute("/product/@id/edit", pageContext.urlPathname);
};
// /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/@view", pageContext.urlPathname);
  if (!["reviews", "pricing"].includes(result.routeParams.view)) {
    return false;
  }
  return result;
};

始终运行

每次渲染页面时(即每次renderPage()调用时),vite-plugin-ssr 都会执行 所有 路由函数

这是因为 vite-plugin-ssr 无法预测路由函数是否会返回一个高优先级数字来覆盖所有其他路由

例如:

// /pages/login.page.route.js

// 将登录页面渲染给未鉴权的用户的路由函数,无论 URL 是什么

export default (pageContext) => {
  // 仅向未鉴权的用户渲染登录页面
  if (userIsLoggedIn(pageContext)) {
    return false;
  }

  return {
    // 我们通过设置 99 的高优先级来覆盖所有其他路由
    // 这意味着 *所有* URL 都会渲染登录页面(如果用户未通过身份验证)
    precedence: 99,
  };
};

function userIsLoggedIn(pageContext) {
  return pageContext.user !== null;
}

这意味着 vite-plugin-ssr 总是必须执行这个路由函数来解析路由

Guides > Page Redirection > Auth Redirection 中进一步解释了这个技巧

异步

异步路由函数可能会明显降低我们的应用程序的速度:正如我们在上一节中看到的那样,每次渲染页面时,我们所有页面的路由函数都会被调用并等待

这意味着缓慢的路由功能会减慢我们所有页面的速度

默认情况下,异步路由功能是被禁止的,但我们可以通过设置 iKnowThePerformanceRisksOfAsyncRouteFunctions 来开启

// *.page.route.js

export const iKnowThePerformanceRisksOfAsyncRouteFunctions = true;

// 现在可以使用异步路由函数
export default async () => {
  /* ... */
};

此外,禁止在路由函数中返回 pageContext。(异步路由函数也是如此)

// *.page.route.js

export const iKnowThePerformanceRisksOfAsyncRouteFunctions = true;

// `vite-plugin-ssr` 会报错
export default async () => {
  return {
    pageContext: {
      // Some additional `pageContext`
    },
  };
};

通常,使用异步路由函数的目的是确定 URL 是否存在:

// user.page.route.js

export const iKnowThePerformanceRisksOfAsyncRouteFunctions = true;

export default async (pageContext) => {
  const { url } = pageContext;

  // Parse the URL
  const urlParts = url.slice(1).split("/");

  // Only URLs that start with `/user/@userId`
  if (urlParts[0] !== "user") return false;
  const userId = urlParts[1];
  if (!userId) return false;

  // Only if there is a user matching `userId`
  const user = await db.fetchUser(userId);
  if (!user) {
    return false;
  }

  return {
    routeParams: {
      userId,
    },
    // `vite-plugin-ssr` 报错
    pageContext: {
      user,
    },
  };
};

相反,我们可以在 onBeforeRender() hook 中throw RenderErrorPage()

// user.page.server.js

export { onBeforeRender }

import { RenderErrorPage } from 'vite-plugin-ssr'

function onBeforeRender(pageContext) {
  const { userId } = pageContext.routeParams

  const user = await db.fetchUser(userId)

  if (!user) {
    // `vite-plugin-ssr` 会渲染 `_error.page.js`
    throw RenderErrorPage({
      pageContext: {
        // 我们可以提供一些额外的 `pageContext` 在 `_error.page.js` 中使用
        errorInfo: `User ${userId} doesn't exist`
      }
    })
  }

  // ...
}

RenderErrorPage() 只能在服务端使用。如果您想在浏览器端使用它,请在 GitHub 上创建一个 Feature Request

// user.page.route.js

export default (pageContext) => {
  const { url } = pageContext;

  const urlParts = url.slice(1).split("/");

  if (urlParts[0] !== "user") return false;
  const userId = urlParts[1];
  if (!userId) return false;

  return {
    routeParams: {
      userId,
    },
  };
};