SPA+SSR 前后端混合渲染的 “Hydration” 问题
Posted | stdout
标题里的 "hydration" 应该来自 React 里 hydrateRoot
这个对象,其本意是如果说服务端模板输出的 html 是静态的,noscript
的,干瘪的,那么 js 的引入就可以让页面变得 “湿润”,“充水”,“流动”,“顺滑” 起来。国内Web社区把这玩意翻译成 “水合”。
作为一个后端仔,这几天小搓了下页面,发现javascript工程化的一个大坑,说说我对 “hydration” 的理解。
以这个vue官方教程为例:
const items = ref([{ message: 'Foo' }, { message: 'Bar' }])
<ul>
<li v-for="item in items">
{{ item.message }}
</li>
</ul>
看上去人畜无害很容易
但是加上 SSR 就复杂了。比如这个 TodoMVC 已经在服务端输出两条了
<ul>
<li>A</li>
<li>B</li>
<li v-for="item in items">
{{ item.message }}
</li>
</ul>
现在如果 items
更新需要重绘,怎么让 v-for
知道前面两个 <li>
的存在呢?
假如这个问题你能解决,浏览器端OK,那么服务端怎么写呢?
<ul>
{% for item in ["A", "B"] %}
<li>{{ item }}</li>
{% endfor %}
<li v-for="item in items">
{{ item.message }}
</li>
</ul>
这样?是不是觉得太丑了,最好把两个逻辑合并呢?
这就是我理解的 hydration 问题。上面只是一个简单例子,复杂的SSR,理论上应该可以精确渲染到页面特定路由的特定状态的精确那一帧。
要达到这个目的,目前普遍做法是在服务端多一个 编译(compile) 过程,其核心无非是在服务器内存里山寨一个 DOM 的层级结构。
感觉应该有更好的做法,但是我没找到。现有的 Vue Vite React Next 都有解,但是我觉得都太重了。在一个 .html 里写几条 directive 修修改改就能把这事儿搞定的办法,目前看来几乎不可能。
累了,毁灭吧。
Comments