渲染上下文
RenderContext 负责服务端渲染(SSR)过程中的资源管理与 HTML 生成,提供模块依赖收集与资源注入顺序约束。
使用方式
通过 esmx.render() 获取实例:
src/entry.node.ts
async server(esmx) {
const server = http.createServer((req, res) => {
esmx.middleware(req, res, async () => {
const rc = await esmx.render({
params: {
url: req.url
}
});
res.end(rc.html);
});
});
}
主要功能
依赖收集
RenderContext 在组件渲染过程中收集模块与资源依赖,避免预加载所有资源。
按需收集
- 在组件实际渲染过程中自动追踪和记录模块依赖
- 只收集当前页面渲染时真正使用到的 CSS、JavaScript 等资源
- 通过
importMetaSet 精确记录每个组件的模块依赖关系
- 支持异步组件和动态导入的依赖收集
自动化处理
- 开发者无需手动管理依赖收集过程
- 框架自动在组件渲染时收集依赖信息
- 通过
commit() 方法统一处理所有收集到的资源
- 自动处理循环依赖和重复依赖的问题
性能优化
- 避免加载未使用的模块,显著减少首屏加载时间
- 精确控制资源加载顺序,优化页面渲染性能
- 自动生成最优的导入映射(Import Maps)
- 支持资源预加载和按需加载策略
资源注入
RenderContext 提供了多个方法来注入不同类型的资源,每个方法都经过精心设计以优化资源加载性能:
preload():预加载 CSS 和 JS 资源,支持优先级配置
css():注入首屏样式表,支持关键 CSS 提取
importmap():注入模块导入映射(Import Maps),支持动态路径解析
moduleEntry():注入客户端入口模块,支持多入口配置
modulePreload():预加载模块依赖,支持按需加载策略
资源注入顺序
RenderContext 严格控制资源注入顺序,这种顺序设计是基于浏览器的工作原理和性能优化考虑:
-
head 部分:
preload():预加载 CSS 和 JS 资源,让浏览器尽早发现并开始加载这些资源
css():注入首屏样式表,确保页面样式在内容渲染时就位
-
body 部分:
importmap():注入模块导入映射,定义 ESM 模块的路径解析规则
moduleEntry():注入客户端入口模块,必须在 importmap 之后执行
modulePreload():预加载模块依赖,必须在 importmap 之后执行
完整渲染流程
一个典型流程如下:
src/entry.server.ts
export default async (rc: RenderContext) => {
const app = createApp();
const html = await renderToString(app, {
importMetaSet: rc.importMetaSet
});
await rc.commit();
rc.html = `
<!DOCTYPE html>
<html>
<head>
${rc.preload()}
${rc.css()}
</head>
<body>
${html}
${rc.importmap()}
${rc.moduleEntry()}
${rc.modulePreload()}
</body>
</html>
`;
};
高级特性
基础路径配置
RenderContext 提供了一个灵活的动态基础路径配置机制,支持在运行时动态设置静态资源的基础路径:
src/entry.node.ts
const rc = await esmx.render({
base: '/esmx',
params: {
url: req.url
}
});
这种机制特别适用于以下场景:
-
多语言站点部署
主域名.com → 默认语言
主域名.com/cn/ → 中文站点
主域名.com/en/ → 英文站点
-
微前端应用
- 支持子应用在不同路径下灵活部署
- 便于集成到不同的主应用中
导入映射模式
RenderContext 提供了两种导入映射(Import Maps)模式:
-
Inline 模式(默认)
- 将导入映射直接内联到 HTML 中
- 适合小型应用,减少额外的网络请求
- 页面加载时立即可用
-
JS 模式
- 通过外部 JavaScript 文件加载导入映射
- 适合大型应用,可以利用浏览器缓存机制
- 支持动态更新映射内容
可以通过配置选择合适的模式:
src/entry.node.ts
const rc = await esmx.render({
importmapMode: 'js',
params: {
url: req.url
}
});
入口函数配置
RenderContext 支持通过 entryName 配置来指定服务端渲染的入口函数:
src/entry.node.ts
const rc = await esmx.render({
entryName: 'mobile',
params: {
url: req.url
}
});
这种机制特别适用于以下场景:
- 多模板渲染
src/entry.server.ts
// 移动端入口函数
export const mobile = async (rc: RenderContext) => {};
export const desktop = async (rc: RenderContext) => {};
-
A/B 测试
- 支持同一页面使用不同的渲染逻辑
- 便于进行用户体验实验
- 灵活切换不同的渲染策略
-
特殊渲染需求
- 支持某些页面使用自定义的渲染流程
- 适应不同场景的性能优化需求
- 实现更精细的渲染控制
最佳实践
-
获取 RenderContext 实例
- 始终通过
esmx.render() 方法获取实例
- 根据需要传入适当的参数
- 避免手动创建实例
-
依赖收集
- 确保所有模块都正确调用
importMetaSet.add(import.meta)
- 在渲染完成后立即调用
commit() 方法
- 合理使用异步组件和动态导入优化首屏加载
-
资源注入
- 严格遵循资源注入顺序
- 不要在 body 中注入 CSS
- 确保 importmap 在 moduleEntry 之前
-
性能优化
- 使用 preload 预加载关键资源
- 合理使用 modulePreload 优化模块加载
- 避免不必要的资源加载
- 利用浏览器缓存机制优化加载性能