Vue 教程

路由

跟 Nuxt 类似, 我们通过创建一个 .page.vue 文件来定义一个新页面

<!-- /pages/index.page.vue -->
<!-- Environment: Browser, Node.js -->

<template>This page is rendered to HTML and interactive: <Counter /></template>

<script setup>
import Counter from "../components/Counter.vue";
</script>

默认情况下,vite-plugin-ssr 执行文件系统路由

FILESYSTEM                  URL
/pages/index.page.vue        /
/pages/about.page.vue        /about

我们也可以使用 路由字符串(参数化路由,如/movies/@id)或 路由函数(灵活编程)来定义页面路由

// /pages/index.page.route.js
// Environment: Node.js (and Browser if we choose Client Routing)

// 请注意这两个文件是如何共享相同的base `/pages/index.page.`;
// `vite-plugin-ssr` 知道 `/pages/index.page.route.js` 定义了 `/pages/index.page.vue` 的路由

// 路由函数
export default (pageContext) => pageContext.urlPathname === "/";

// 如果我们不创建 `.page.route.js` 文件,那么 `vite-plugin-ssr` 会执行文件系统路由

控制渲染

跟 Nuxt 不同的是,我们可以控制如何页面渲染

// /renderer/_default.page.server.js
// Environment: Node.js

import { createSSRApp, h } from "vue";
import { renderToString } from "@vue/server-renderer";
import { escapeInject, dangerouslySkipEscape } from "vite-plugin-ssr";

export { render };

async function render(pageContext) {
  const { Page, pageProps } = pageContext;
  const app = createSSRApp({
    render: () => h(Page, pageProps),
  });

  const appHtml = await renderToString(app);

  const title = "Vite SSR";

  return escapeInject`<!DOCTYPE html>
    <html>
      <head>
        <title>${title}</title>
      </head>
      <body>
        <div id="app">${dangerouslySkipEscape(appHtml)}</div>
      </body>
    </html>`;
}
// /renderer/_default.page.client.js
// Environment: Browser

import { createSSRApp, h } from "vue";

export { render };

async function render(pageContext) {
  const { Page, pageProps } = pageContext;
  const app = createSSRApp({
    render: () => h(Page, pageProps),
  });
  app.mount("#app");
}

你可能第一次看到 import { createSSRApp, h } from 'vue', 但实际上每个 SSR 应用都使用 createSSRApph

Nuxt 将这一点抽象出来,让我们可以更快上手,但也意味着我们失去了对应用程序架构核心部分的控制, 我们最终会在 Nuxt 限制性的黑盒上失去更多时间

这种级别的控制使我们 轻松自然 地集成我们想要的任何工具(Vuex, GraphQL, Service Worker, ...)

页面文件后缀有四种:

  • .page.js: 在浏览器和 Node.js 中运行
  • .page.client.js: 只在浏览器中运行
  • .page.server.js: 只在 Node.js 中运行
  • .page.route.js: 定义页面的路由字符串或路由函数

我们不必为每个页面创建 .page.client.js.page.server.js 我们可以创建一个默认应用到所有页面的 /renderer/_default.page.client.js/renderer/_default.page.server.js

实际上我们最后创建的两个文件是 /renderer/_default.page.client.js/renderer/_default.page.server.js 这意味着我们可以通过定义一个新的 .page.vue 文件来创建一个新的页面(.page.route.js文件是可选的)

_default.page. 文件可以被覆盖。比如,我们可以在一些页面中使用不同的 UI 框架(如 React)覆盖 render() hook

数据获取

现在我们来看看如何获取数据

<!-- /pages/star-wars/movie.page.vue -->
<!-- Environment: Browser, Node.js -->

<template>
  <h1>{{ movie.title }}</h1>
  <p>Release Date: {{ movie.release_date }}</p>
  <p>Director: {{ movie.director }}</p>
</template>

<script lang="js">
const pageProps = ['movie']
export default { props: pageProps }
</script>
// /pages/star-wars/movie.page.route.js
// Environment: Node.js

// 路由字符串
export default "/star-wars/@movieId";
// /pages/star-wars/movie.page.server.js
// Environment: Node.js

import fetch from "node-fetch";

export async function onBeforeRender(pageContext) {
  // `/star-wars/@movieId` 的路由参数在 `pageContext.routeParams` 中
  const { movieId } = pageContext.routeParams;

  // `.page.server.js` 文件总是运行在 Node.js 环境中;在这里我们可以使用 SQL/ORM 查询
  const response = await fetch(`https://swapi.dev/api/films/${movieId}`);
  let movie = await response.json();

  // 我们之前定义的 render 和 hydrate 函数将 `pageContext.pageProps` 传递给Vue根组件 `Page`
  // 这就是我们定义 `pageProps` 的地方
  const pageProps = { movie };

  // 我们将 `pageProps` 作为 `pageContext.pageProps` 提供出去
  return {
    pageContext: {
      pageProps,
    },
  };
}

// 默认情况下,`pageContext.*` 只能在服务器环境上使用,但我们之前定义的 hydrate 函数
// 在浏览器中运行并且需要 `pageContext.pageProps`;
// 我们使用 `passToClient` 序列化 `pageContext.pageProps`
// 并让它在浏览器中可用
export const passToClient = ["pageProps"];

这就是教程的内容,实际上我们已经了解了大部分接口 vite-plugin-ssr 不仅灵活,而且使用简单!

$ npm init vite-plugin-ssr
运行 $ npm init vite-plugin-ssr 初始化 Vite/vite-plugin-ssr 应用, 或添加vite-plugin-ssr 到已有应用程序中。
说明文档