SSR 模式引入的问题

两个问题

获取数据的代码可能执行在服务端,也可能执行在客户端。而服务端可以直接连接db,客户端并不能。当用户执行了action(提交表单给服务端)之后,哪些界面区块需要更新。

各种框架的解决方案

经典 hydration 模式:比如 vue 或者 react 的 hydration apiremixqwikhotwire.dev

经典 hydration 是最主流的。获取数据假定都在客户端发生,和普通的客户端渲染没区别。界面区块刷新也是依赖客户端渲染模式的依赖订阅,所以必须要客户端重走一遍渲染,把依赖关系给订阅上。基本上就是客户端渲染,只是 SSR 给打了点补丁。

remix 提供了服务端 loader 的抽象。可以把连数据库获取数据的代码都放 loader 里。客户端要刷新区块了,会走 remix 暴露的 http 路由访问到对应的服务端 loader 去获取数据。从而就不用区分是从客户端加载数据还是服务端加载数据,都是在服务端。区块刷新问题走最暴力的全刷新模式,没有任何依赖订阅的优化。

qwik 对于数据加载没有偏好,同经典模式。hydration 是 lazy 的。lazy 的原理是类似 vue 的 es6 proxy 订阅。只是这个订阅关系是在服务端渲染的时候被记录,然后持久化到 ssr 的 html 内,在客户端再被拿来用。相比 remix 而言是另外一个极端。btw:qwik 实现了一套自己的 vdom 和类似 vue 的依赖跟踪。

hotwire.dev 或者说 turbo-stream 尝试去解决这两个问题。turbo-stream 是纯服务端的数据加载,以及所有 UI 都是在服务端渲染出来,所以没有客户端需要去获取数据的问题。而界面区块更新问题则是通过开发者指定哪些区块需要更新来解决的。而且不是“刷”的模式,而是要开发者手写增量的 diff。这这就是类“jquery”而不是声明式的了。

总结

如果一个用户操作,需要触碰到数据库。那么这个用户操作的处理代码,重渲染代码由服务端来执行无可厚非。并不存在因为“服务端渲染”了,所以导致用户交互不流畅,动画卡顿的问题。因为业务逻辑就要求动数据库,怎么也绕不过RPC了。所以对于问题1而言,应该是尽可能多的把代码放服务端,remix 或者 turbo-stream 这种

对于要刷新的区块选择而言。remix 的全刷模式太暴力了,而 qwik 又过于精细。turbo-stream 这种开发者手写增量的过气写法就更不用考虑了。对于问题2,应该是走声明式 UI 的模式,只是订阅不用做到 vue 那样 object 级别的精细。

qwik 之所以需要精确依赖追踪,核心是 qwik 不假定其 event handler 一定会是 RPC 这么重型的操作。可能就是 toggle panel 之类的纯客户端的 state change。而这种精细化的 UI 改动,经典的 hydration 都是通过重新在客户端跑一遍渲染来实现的。如果把 event handler 分成需要 RPC 和不需要 RPC 的,在 SSR 的前提下,就会非常不一样了。也就是类似 hotwire.dev 提出的 stimulus 那样,需要服务端处理的走一套,如果只是操作纯客户端状态的走另外一套。