这篇文档是关于 Respo 服务端渲染(SSR)方案的一个介绍
最新内容参考 GitHub 的英文文档 https://github.com/Respo/resp...

因为 Respo 也是基于 Virtual DOM 的方案, 所以也能做服务端渲染,
为了方便, 渲染过程用的是 Planck, 而不是直接调用 Boot.
Respo SSR 的内容是承接前面的 Vue SSR 以及渐进首屏渲染的.

在前面已经完成了 ssr-stages 这个仓库可以演示:
https://github.com/Respo/ssr-...
后面我还用 http://tiye.me/ 做了一个测试, 基本上已经稳定了.

思路

原先我们觉得 React 服务端渲染就是在服务端渲染第一屏, 返回给浏览器,
但是这不能解决一些稍微复杂的问题, 比如说, 并不渲染整屏,
比如说服务端性能有限, 只渲染头部, 或者只想渲染一个 Shell 的页面,
甚至可能由于对浏览器 API 依赖, 某些局部是无法渲染的,
可能还有 SSR 相关的因素, 影响到这个页面的配置,
于是服务端渲染的首屏其实和前端的首屏, 往往是存在不一致的.

那么我觉得首屏其实是一个 patch 操作, 因为服务端渲染内容和客户端不一样,
既然要 Patch, 就需要有两个 Virtual DOM 用来产生 Diff,
所以我的办法就是让前端伪造一份与 HTML 相对应的 Virtual DOM,
至于怎么生成, 需要用户的业务代码当中完成, 一般还是通过数据控制的.
其实后端渲染相当于前端渲染内容的局部, 所以通过 if 等函数控制下就好了.

由于服务端渲染不方便绑定事件, 特别是不冒泡的事件,
那么我假设服务端 HTML 没有事件, 于是伪造的 Virtual DOM 就要去掉事件,
前端进行 patch 时, 再把事件全部绑定上去,
而这一个功能对于 DOM diff 来说是支持的功能之一, 因而很简单.

另一个要注意的是前端也许需要兼容多个版本的服务端渲染,
因为服务端可能进行控制, 只渲染局部, 或者不渲染, 而前端代码只有一份,
所以伪造的 Virtual DOM 需要自动选择, 当然也需要一些标记,
比如我在 <head> 中放置一个 <meta> 标签, content 属性中写一些信息.
然后前端代码就可以根据这个信息识别, 服务端生成的是哪个版本的 Virtual DOM.
当然, 也许写在 Store 当中也是一个办法, 只是要处理下没有 Store 的情况.

实现

我给了一个例子演示具体的方案:

(defonce global-states (atom {}))

(defn -main []
  (if (not (empty? ssr-stages))
    (let [target (.querySelector js/document "#app")]
      (falsify-stage!
        target
        (mute-element
          (render-element
            (comp-container @store-ref ssr-stages)
            global-states))
        dispatch!)))
  (render-app!)
  (add-watch store-ref :changes render-app!)
  (add-watch states-ref :changes render-app!))

其中具体的调用, 大概是这个意思:

  • (comp-container store level-of-ssr) 用来生成组件

  • (render-element component global-states) 用来渲染组件

  • (mute-element element) 移除掉组件中的事件

  • (falsify-stage! target element dispatch!) 在内部伪造 Virtual DOM

这样一个过程完成之后, Respo 就认为前端已经有 Virtual DOM 的状态了,
然后再渲染时, 就不在使用 mount, 自动进入到 Patch 的模式当中.
因而首屏渲染的问题基本上就解决了.

小结

目前 React 和 Vue 都没有这个功能, 他们都支持强制覆盖掉本地的 DOM.
其实也是个办法, 除了一些动画的细节会被破坏.. 以及可能有额外的抖动,
希望未来这可以是一个很多框架都支持的功能.


题叶
17.3k 声望2.6k 粉丝

Calcit 语言作者


引用和评论

0 条评论