几年前在这发过我写的 Web Components 库 Gem ( https://v2ex.com/t/738230 ),一直有个硬伤:只有客户端渲染。连它自带的文档站生成器 gem-book 也是 CSR ,搜索引擎基本收录不到啥东西。
之前在这问过 React SSR 和自定义元素怎么配合( https://v2ex.com/t/998353 ),当时我自己回复说「用 jsdom 模拟生成 ShadowDOM 理论上也行」。后来真这么干了:
- 先用 jsdom 做原型,跑通了序列化(输出 Declarative Shadow DOM )和水合(复用服务端的 DOM ,不重建)
- 再把 jsdom 去掉,直接渲染成字符串,用 AsyncLocalStorage 做请求隔离,支持流式输出,这就是 gem-ssr
- 最近参考 Next.js 和 SvelteKit ,在 gem-ssr 上做了应用框架 Gemkit
长啥样
一个文件就是一个页面:
// src/pages/blog/[slug].ts
@page<{ slug: string }, Post>({
load: ({ params }) => getPost(params.slug),
title: ({ data }) => data.title,
})
export default class PostPage extends GemElement {
@property data?: Post;
@template()
#content = () => html`<h1>${this.data?.title}</h1>`;
}
只在服务端跑的代码放同名的 [slug].server.ts ,可以读 cookie 、redirect 、处理表单 actions ,不会打包到客户端。用过 SvelteKit 的应该很熟悉。
其他能力:
- 文件路由、嵌套布局、i18n
- 默认 SSG ,部署静态托管就行;需要的页面可以按请求渲染,也支持 ISR
@server()元素只在服务端渲染,代码和依赖不进客户端;@lazy()元素用户交互时才加载代码水合,第一次点击不会丢- 本身就是个 Rsbuild 插件,Rsbuild 的东西照常用
- 内置 Vercel / Netlify / Cloudflare 适配
和其他框架比了下
用 RealWorld ( Medium 克隆)各实现了一遍,同一个后端、同一份数据:
| 首页 JS (gzip) | Lighthouse | FCP | SSR req/s | |
|---|---|---|---|---|
| Gemkit | 25.9 KB | 100 | 906 ms | 1382 |
| SvelteKit | 44.0 KB | 100 | 1285 ms | 1437 |
| Nuxt | 85.8 KB | 99 | 1656 ms | 1042 |
| Next.js | 103.5 KB | 96 | 908 ms | 317 |
SSR 吞吐 SvelteKit 略高一点,两个其实都快到后端的上限了。测试代码、复现步骤和原始数据都在这: https://github.com/mantou132/realworld/tree/framework-comparison/frameworks ,觉得测得不对的欢迎指出来。
不足
- 个人项目,还是 0.x ,没在大项目里用过
- 客户端导航是先改 URL 再渲染新页面,加载期间会看到新 URL + 旧页面,后面再改
试试
npm create gemkit@latest my-app
- 文档: https://gemkit.gemjs.org/zh
- 仓库: https://github.com/mantou132/gem
- 完整的过程写了篇文章(英文): https://dev.to/mantou132/from-client-only-to-ssr-building-gemkit-a-sveltekit-style-framework-for-web-components-8ag
欢迎拍砖,觉得有意思的话点个 star 吧。