@malla/renderer 是一个从文档到 HTML 和 CSS 的纯函数。它在画布框架、sites Worker 和测试中运行,这正是编辑器与生产环境一致的原因。
ts
renderPage({ doc, components, classes, tokens, site, data, user? })
→ { html, css, runtime: RuntimeManifest }
快照发布
apiPOST /sites/:id/publish 检查角色和套餐。
快照snapshot.json 和编译好的共享 CSS 写入 R2。
工作流服务端工作流按环境写入 KV。
切换KV 主机指针 → 新的 publishId。
一次发布会写入一个不可变的快照(页面和组件文档、类、令牌、SEO、清单),然后切换一个指针,因此发布是瞬间且原子的,回滚也只是再切换一次。发布弹出框和 POST /sites/:siteId/rollback 可以把它切回该环境最近 50 次发布中的任意一次(参见回滚);CMS 条目、资源和其他实时数据不会回滚。关键页面会被预渲染,因此永远不会冷启动。
按需渲染
主机查找KV:主机 → 站点、环境、publishId。
访问控制每次请求都检查页面规则。
缓存命中:Cache API 或 R2 回写。
渲染未命中:从快照执行 renderPage。
- 静态页面每次发布渲染一次,并以包含
publishId的键永久缓存。 - 服务端页面在边缘按请求运行服务端查询并渲染,TTL 较短。
- CMS 改动会递增每个集合的修订号;只有依赖它的页面会重新渲染。
/api/*和/_auth/*交给 runner;/api/_q/*(客户端查询)和/api/_forms/*留在 sites 中处理。
岛屿运行时
发布出去的结构是静态 HTML。只有绑定的文本、属性、样式和重复列表会变化,因此 @malla/runtime 就是 @vue/reactivity + 共享的表达式解释器 + 一个极简的 DOM 补丁器:没有虚拟 DOM,没有完整激活(hydration),gzip 后不到 25 KB。它只在带有绑定、表单或客户端工作流的页面上加载;GSAP 是一个单独的代码块,只在有交互时加载。
- 渲染器会输出一份岛屿清单,说明需要激活哪些部分。
- 按请求渲染的页面把访客信息嵌入清单;缓存的页面只在有内容读取
user时,才在启动时请求一次/_auth/me。 - 多页面导航使用 Speculation Rules 预取和 View Transitions,带来类似应用的体验。
在画布中
每个画板都是一个位于独立源上的沙箱 iframe,通过带类型的 postMessage 协议与编辑器通信:初始化、用 ops 打补丁、测量、命中测试。补丁只触及发生变化的节点及其 CSS 规则。