一体化 SSG
SSG(Static Site Generation)是一种基于数据与模板,在构建时渲染完整静态网页的技术解决方案。
注
SSG 是构建阶段的解决方案,因此仅对生产环境有效。通过 dev
命令运行时,表现效果与 SSR 相同。
我们首先需要执行pnpm run new
启用 SSG 功能:
? 请选择你想要的操作 启用可选功能
? 启用可选功能 启用「SSG」功能
SSG 在约定式路由和自控式路由下的使用方式不同。
在约定式路由中使用
约定式路由中, Modern.js 根据入口下的文件结构生成路由,因此框架能够收集完整的路由信息。
例如,以下是一个使用约定式路由的项目目录结构:
├── src/
│ ├── pages/
│ │ ├── home.tsx
│ │ ├── info.tsx
│ │ └── index.tsx
在 src/pages/index.tsx
中添加组件代码:
export default () => {
return <div>index page</div>
}
SSG 和 SSR 一样,也是在 Node.js 环境中完成页面渲染,因此我们可以在开发阶段开启 SSR,提前在开发阶段验证 SSG 渲染效果:
export default defineConfig({
server: {
ssr: process.env.NODE_ENV === 'development',
}
}
在项目根路径下执行 pnpm run dev
命令,查看 dist/
目录,此时只生成一个 HTML 文件:
在项目根路径下执行 pnpm run build
命令,构建完成后,查看 dist/
目录,此时生成 main/index.html
、main/home/index.html
和 main/info/index.html
3 个 HTML 文件,分别对应 /
、/home
、/info
3 个约定式路由,可见约定式路由中的每一条路由,都会生成一个单独的 HTML 文件。查看 main/index.html
,可以发现包含 index page
的文本内容,这正是 SSG 的效果。
执行 pnpm run start
启动项目后,访问页面,在浏览器我们工具的 Network 窗口,查看请求返回的文档,文档包含组件渲染后的完整页面内容。
在自控式路由中使用
自控式路由是通过组件代码自定义路由,需要应用运行起来才能获取准确的路由信息。因此,无法开箱即用的使用 SSG 功能。此时需要用户提前告知 Modern.js 框架,哪些路由需要开启 SSG 功能。
例如有以下代码:
import { Switch, Route } from '@modern-js/runtime/router';
export default () => (
<Switch>
<Route path="/" exact={true}>
<div>Home</div>
</Route>
<Route path="/foo" exact={true}>
<div>Foo</div>
</Route>
</Switch>
);
希望为 /foo
路由开启 SSG 功能,同样利用 output.ssg
配置。该参数允许配置页面信息,告知 Modern.js 开启指定路由的 SSG 功能。
例如上述入口中,包含两条客户端路由,分别是 /
和 /foo
,设置 output.ssg
为 true
时,默认只会渲染入口路由即 /
。如果我们希望同时开启 /foo
的 SSG 功能,可以这样配置:
export default defineConfig({
output: {
ssg: {
routes: ['/', '/foo'],
},
},
})
执行 pnpm run build
与 pnpm run start
后,访问 http://localhost:8080/foo
,在 Preview 视图中可以看到页面已经完成渲染。
查看构建产物文件,可以看到 dist/
目录中,在默认的 main
入口产物目录下,新增一个 foo.html
文件:
info
以上仅介绍了单入口的情况,与部分配置。更多相关内容可以查看 API 文档。
获取数据
在 SSR 中,组件可以通过 useLoader
同构的获取数据。在 SSG 中,Modern.js 也提供了相同的能力。
调用 useLoader
时,在第二个参数中设置 { static: true }
,可以在 SSG 阶段执行数据的请求。
注
- Modern.js 目前还不支持 SSG 与 SSR 混合渲染,敬请期待。
- 在开发阶段,不管
useLoader
是否配置{ static: true }
,函数都会在 SSR 时获取数据。
修改上述 App.ts
的代码为:
import { Switch, Route } from '@modern-js/runtime/router';
import { useLoader } from '@modern-js/runtime';
export default () => {
const { data } = useLoader(
async () => ({
message: Math.random(),
}),
{
static: true,
params: 'foo',
},
);
return (
<Switch>
<Route path="/" exact={true}>
<div>Home</div>
</Route>
<Route path="/foo" exact={true}>
<div>Foo, {data?.message}</div>
</Route>
</Switch>
);
};
执行 pnpm run dev
,重复刷新页面,可以看到 /foo
页面的渲染结果不断发生变化,说明数据是在请求时获取的。
重新执行 pnpm run build
后,执行 pnpm run start
,重复刷新页面,发现页面渲染结果始终保持同样的内容,说明数据在请求时不会再次获取。