渲染上下文

我们有 entry.node.tsentry.server.ts 两个入口文件,entry.node.ts 负责创建服务器,来调用 entry.server.ts 生成 HTML。为了简化 CSS 和 JS 的注入,于是提供了一个 RenderContext 对象。

基本传参

entry.node.ts 通常可以看到这样的代码,调用一个渲染函数,然后服务响应 HTML。

entry.node.ts
1const rc = await gez.render({
2    // 传入渲染的参数
3    params: {
4        url: req.url
5    }
6});
7// 响应 HTML 内容
8res.end(rc.html);

entry.server.ts 接收到传入的参数,并且根据传入的参数来响应内容。

entry.server.ts
1import type { RenderContext } from '@gez/core';
2
3export default async (rc: RenderContext) => {
4    // 传入的参数
5    console.log(rc.params);
6    // 提交模块依赖收集
7    await rc.commit();
8    // 响应内容
9    rc.html = `你好世界!`;
10}

模块依赖收集

在 SSR 应用程序中,要处理注入渲染页面的 CSS 和 JS 文件,并不是一件简单的事情,当需要考虑多服务提供的模块时,这个问题将会变得更难。庆幸的是,Gez 提供了一个标准的实现方案,并且在 Vue 中提供了完整的实现。

在构建阶段,Gez 会给服务端生成的每一个 JS 文件头部注入一个 import.meta.chunkName 字段,该字段提供了一个 chunk 文件打包的入口源文件。

[chunk].js
1import.meta.chunkName= import.meta.chunkName ?? "ssr-vue2-remote@src/entry.ts";

ssr-vue2-remote 是我们的服务名,src/entry.ts 是这个文件打包的第一个文件。

例如:

1import('ssr-vue2-remote/src/app.ts');

上述代码,就会生成

1import.meta.chunkName = import.meta.chunkName ?? "ssr-vue2-remote@src/app.ts";

Vue2 例子

将渲染上下文的 importMetaSet 对象传递给 Vue SSR 渲染的上下文对象中。

entry.server.ts
1export default async (rc: RenderContext) => {
2    const { app } = createApp();
3
4    const html = await renderer.renderToString(app, {
5        importMetaSet: rc.importMetaSet
6    });
7    // ......
8};

在 Vue 组件中,收集上下文依赖。

App.vue
1export default {
2    serverPrefetch () {
3        this.$ssrContext?.importMetaSet.add(import.meta);
4    }
5}
TIP

在实际的操作中,@gez/rspack-vue 已经在编译 .vue 组件时,已经将这段代码注入,你不需要手动调用。

等 Vue 组件渲染完成后,调用 await rc.commit() 函数来提交模块上下文的依赖收集, rc.preload()rc.css()rc.importmap()rc.modulePreload() 才能正确的注入客户端所需的依赖。

entry.server.ts
1export default async (rc: RenderContext) => {
2    // ......
3    await rc.commit();
4
5    rc.html = `
6<!DOCTYPE html>
7<html>
8<head>
9    ${rc.preload()}
10    <title>Gez</title>
11    ${rc.css()}
12</head>
13<body>
14    ${html}
15    ${rc.importmap()}
16    ${rc.moduleEntry()}
17    ${rc.modulePreload()}
18</body>
19</html>
20`;
21};

深入了解

TIP

如果你想更加深入了解模块依赖收集,可以看下 ssr-html 这个例子,它是通过编码的形式来实现模块的依赖收集。