如题,暑假归来,俺终究是给 fre 搞了 ssr,这篇文章详细阐述一下
- node 独立开发
目前的独立开发,node 世界, 基本就是清一色的 ssr 框架,比如 next,nuxt,remix 之类的
但因为 RSC 这些 react19 新特性,各种 use client,use server 语法糖,搞的现在的 ssr 框架,难用的一批
所以俺决定给 fre 支持一下 ssr,主打一个心智负担小
- 传统 ssr
传统的 ssr,就是一个 rendertostring,将 vdom 渲染为字符串,然后客户端再进行水合
同样的道理,hooks 需要实现一遍,规则如下:
useState - server/client
useMemo/useCallback - server/client
useContext - server/client
use - server/client
useId - sever/client
useRef - server/client(但server的current没有dom引用)
useEffect - client
lazy - client
为了支持 ssr,特地实现了一个 useAction,这是个 server only 的 hook
完整 demo 如下:
import { h, useState } from 'npm:fre'
import { renderToString, useAction } from 'npm:fief'
const html = (freCode) => {
return `
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Fre SSR demo</title>
</head>
<body>
${freCode}
</body>
</html>
`
}
function App() {
const [posts, setPosts] = useState([])
useAction(async () => {
const { data } = await fetch('https://www.clicli.cc/rank?day=100').then(res => res.json())
setPosts(data)
})
return (
<div>
{posts.map(item => (
<li key={item.id}>{item.title}</li>
))}
</div>
)
}
Deno.serve(async (req) => {
const content = await renderToString(<App /> as any)
return new Response(html(content), {
headers: {
"content-type": 'text/html;charset=utf-8',
"Access-Control-Allow-Origin": "*"
}
})
})
怎么样,是不是,very easy
- 基于 suspense 边界的 ssr
react18 以后,他们找到了一种新的 ssr 策略,就是使用 suspense 进行边界划分
然后通过 node stream 进行流式发送,先发送 loading,然后再发送真实内容+水合代码,这样一来,就可以做到局部水合了
值得一提的是,react 的局部水合和 islands 不同,它是进行全量遍历,然后遍历到了 server 端完成状态的 suspense 就直接跳过
这个方案俺也研究了一波,但想了想还是算了,因为 deno 这些平台对 web stream 的支持还比较有限,而且心智成本比较高
poc 代码在这里:gist.github.com/yisar/83632…
- RSC
没啥卵用,目前能想到的,可能就是做加密比较有用,以往前端加密不靠谱的,放到 server component 中做可能就靠谱了,除此之外好像别无用处
0js 根本就是个伪命题
- 总结
以上,整体代码在这里: github.com/frejs/fief
demo在这里:fre-ssr.yisar.deno.net