fre 支持 ssr

156 阅读2分钟

如题,暑假归来,俺终究是给 fre 搞了 ssr,这篇文章详细阐述一下

  1. node 独立开发

目前的独立开发,node 世界, 基本就是清一色的 ssr 框架,比如 next,nuxt,remix 之类的

但因为 RSC 这些 react19 新特性,各种 use client,use server 语法糖,搞的现在的 ssr 框架,难用的一批

所以俺决定给 fre 支持一下 ssr,主打一个心智负担小

  1. 传统 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

  1. 基于 suspense 边界的 ssr

react18 以后,他们找到了一种新的 ssr 策略,就是使用 suspense 进行边界划分

然后通过 node stream 进行流式发送,先发送 loading,然后再发送真实内容+水合代码,这样一来,就可以做到局部水合了

值得一提的是,react 的局部水合和 islands 不同,它是进行全量遍历,然后遍历到了 server 端完成状态的 suspense 就直接跳过

这个方案俺也研究了一波,但想了想还是算了,因为 deno 这些平台对 web stream 的支持还比较有限,而且心智成本比较高

poc 代码在这里:gist.github.com/yisar/83632…

  1. RSC

没啥卵用,目前能想到的,可能就是做加密比较有用,以往前端加密不靠谱的,放到 server component 中做可能就靠谱了,除此之外好像别无用处

0js 根本就是个伪命题

  1. 总结

以上,整体代码在这里: github.com/frejs/fief

demo在这里:fre-ssr.yisar.deno.net