静态站点生成(SSG)

SSG(Static Site Generation)是一种基于数据与模板,在构建时渲染完整静态网页的技术解决方案。这意味着在生产环境中,页面默认就是有内容的,并且可以被 CDN 缓存。对于无需数据的页面,SSG 可以提供更好的性能和更高的安全性。

启用 SSG

我们首先需要执行 pnpm run new 启用 SSG 功能:

? 请选择你想要的操作 启用可选功能
? 请选择功能名称 启用「SSG」功能

执行命令后,在 modern.config.ts 中注册 SSG 插件:

modern.config.ts
import { ssgPlugin } from '@modern-js/plugin-ssg';

export default defineConfig({
  output: {
    ssg: true,
  },
  plugins: [..., ssgPlugin()],
});

开发环境调试

SSG 也是在 Node.js 环境渲染页面,因此我们可以在开发阶段开启 SSR,提前暴露代码问题,验证 SSG 渲染效果:

modern.config.ts
export default defineConfig({
  server: {
    ssr: process.env.NODE_ENV === 'development',
  }
}

在约定式路由中使用

约定式路由中,Modern.js 根据入口下的文件结构生成路由,因此框架能够收集完整的路由信息。

基本用法

例如,以下是一个使用约定式路由的项目目录结构:

.
└── routes
    ├── layout.tsx
    ├── page.tsx
    └── user
        ├── layout.tsx
        ├── page.tsx
        └── profile
            └── page.tsx

上述文件目录将会生成以下三条路由:

  • /
  • /user
  • /user/profile
TIP

如果还不了解约定式路由的规则,可以先查看路由方案

src/routes/page.tsx 中添加组件代码:

src/routes/page.tsx
export default () => {
  return <div>Index Page</div>;
};

在项目根路径下执行 pnpm run dev 命令,查看 dist/ 目录,此时只生成一个 HTML 文件 main/index.html

在项目根路径下执行 pnpm run build 命令,构建完成后,查看 dist/ 目录,此时生成 main/index.htmlmain/user/index.htmlmain/user/profile/index.html 三个 HTML 文件,内容分别对应上述三条路由。

约定式路由中的每一条路由,都会生成一个单独的 HTML 文件。查看 main/index.html,可以发现包含 Index Page 的文本内容,这正是 SSG 的效果。

执行 pnpm run serve 启动项目后,访问页面,在浏览器我们工具的 Network 窗口,查看请求返回的文档,文档包含组件渲染后的完整页面内容。

阻止默认行为

默认情况下,约定式路由的路由会全部开启 SSG。Modern.js 提供了另一个字段,用来阻止默认的 SSG 行为。

例如以下目录结构,//user/user/profle 三条路由都开启 SSG:

.
├── src
│   └── routes
│       ├── layout.tsx
│       ├── page.tsx
│       └── user
│           ├── layout.tsx
│           ├── page.tsx
│           └── profile
│               └── page.tsx

可以通过配置 preventDefault 来禁用某些路由的默认行为。进行下面配置后,最终只会生成 //user/profle 两条路由的 SSG 页面:

export default defineConfig({
  output: {
    ssg: {
      preventDefault: ['/user'],
    },
  },
});

在自控式路由中使用

自控式路由是通过组件代码定义路由,需要应用运行起来才能获取准确的路由信息。因此,无法开箱即用的使用 SSG 功能。开发者需要通过配置告知 Modern.js 框架,哪些路由需要开启 SSG 功能。

例如有以下代码,包含多条路由,设置 output.ssgtrue 时,默认只会渲染入口路由即 /

src/App.tsx
import { BrowserRouter, Route, Routes } from '@modern-js/runtime/router';
import { StaticRouter } from '@modern-js/runtime/router/server';
import React from 'react';
import { useRuntimeContext } from '@modern-js/runtime';

const Router = typeof window === 'undefined' ? StaticRouter : BrowserRouter;

export default () => {
  const { context } = useRuntimeContext();
  const pathname = context?.request?.pathname;
  return (
    <Router location={pathname}>
      <Routes>
        <Route index element={<div>index</div>} />
        <Route path="about" element={<div>about</div>} />
      </Routes>
    </Router>
  );
};

如果我们希望同时开启 /about 的 SSG 功能,可以配置 output.ssg

modern.config.ts
export default defineConfig({
  output: {
    ssg: {
      routes: ['/', '/about'],
    },
  },
});

执行 pnpm run build 后,可以看到 dist/ 目录中,新增了一个 main/about/index.html 文件。

执行 pnpm run serve 启动项目后,访问页面,在浏览器我们工具的 Network 窗口,查看请求返回的文档,文档包含组件渲染后的完整页面内容。

INFO

以上仅介绍了单入口的情况,更多相关内容可以查看 API 文档

添加路由参数

在 Modern.js 中,部分路由可能是动态的,例如自控式路由中的 /user/:id 或是约定式路由中 user/[id]/page.tsx 文件生成的路由。

可以在 output.ssg 中配置具体的参数,渲染指定参数的路由,例如:

export default defineConfig({
  output: {
    ssg: {
      routes: [
        {
          url: '/user/:id',
          params: [{
            id: 'modernjs',
          }],
        },
      ],
    },
  },
});

此时,/user/modernjs 路由会被渲染,并且在渲染时,会将 id 参数传递给组件。当配置多个值时,对应会生成多张页面。

NOTE

动态路由和 SSG 的组合,在根据 CMS 系统数据变更,实时生成静态页面时非常有用。

配置渲染请求头

Modern.js 支持为具体入口或路由配置请求头,例如:

export default defineConfig({
  output: {
    ssg: {
      headers: {
        "x-tt-env": "ppe_modernjs"
      },
      routes: [
        '/',
        {
          url: '/about',
          headers: {
            "from": "modern-website"
          },
        },
      ],
    },
  },
});

上述配置中,为所有路由设置了 x-tt-env 请求头,单独为 /about 路由设置了 from 请求头。

TIP

路由中设置的 headers 会覆盖入口中设置的 headers