• 作者:老汪软件技巧
  • 发表时间:2024-11-01 21:02
  • 浏览量:

思维导图

image.png

优势分析1. #静态资源 0 Bundle Size

减少整体 JS bundle size。React 团队认为使用 Server Component 后包体积能减少 30%。

下面这张图估计每个前端项目都见识过:

Amount of Scripts Downloaded on The Browser from https://www.freecodecamp.org/news/how-to-use-react-server-components/

React Server Component 是如何做到 Zero-Bundle-Size?只使用一次的库直接在产物中去掉。针对静态内容,RSC 直接下发计算好或者说构建好的 HTML 片段(准确来说是流),比如将 map 循环直接“展开”成多个相同 HTML 片段,有点类似 Rust 的 0 成本抽象 Zero Cost Abstraction。

示例 1:map 展开

// Note.js - Server Component
import db from 'db'; 
async function Note(props) {
  const notes = await db.posts.list();
  
  return (
    <main>
      { notes.map(note => <h2>{note.title}h2>) }
    main>
  );
}

发给客户端的 HTML 片段:

<main>
  <h2>Mastering React Hooks: A Comprehensive Guideh2>
  <h2>Building Responsive UIs with React and CSS Gridh2>
  <h2>Optimizing React Apps: Performance Tips and Best Practicesh2>
main>

示例 2:笨重的 Markdown 渲染组件

客户端组件:

// NOTE: *before* Server Components
import marked from 'marked'; // 35.9K (11.2K gzipped)
import sanitizeHtml from 'sanitize-html'; // 206K (63.3K gzipped)
function NoteWithMarkdown({text}) {
  const html = sanitizeHtml(marked(text));
  return (/* render */);
}

RSC:

// Server Component === zero bundle size
- import marked from 'marked'; // zero bundle size
- import sanitizeHtml from 'sanitize-html'; // zero bundle size
function NoteWithMarkdown({text}) {
  // same as before
}

示例 3:利用体积庞大的 moment 时间格式化

import React from 'react'
import moment from 'moment'
export default function Header() {
    return (
      <header>
        <div> It's {moment().format('DD/MM/YYYY hh:mm')}div>
      header>
    );
}

不会把笨重的 moment 打包到 JS,最终发送给客户端的只有 HTML 片段:

<header>
  <div> It's 25/11/2024 17:08div>
header>

当然这个例子也揭露了 RSC 的一个弱项:只能针对静态内容做 bundle size 的优化。

实际上不是直接发送 HTML 而是通过流的形式传输数据。也就是 RSC 并不对 SEO 友好。

https://thoughtbot.com/blog/should-you-react-on-the-server

里面包含构建 html 的虚拟 dom 结构以及数据。

为什么不能是 HTML 片段,因为需要保持已有 DOM 元素的状态。比如输入框聚焦态、输入框正处于输入状态、或者元素正在进行 CSS 动画。

如何阅读 RSC 流协议

RSC是一种按行分隔的数据结构(方便按行流式传输),每行的格式为:

[标记][id]: JSON数据

其中:

示例:

M1:{"id":"./src/ClientCpn.client.js","chunks":["client1"],"name":""}
J0:["$","div",null,{"className":"main","children":["$","@1",null,{"children":["$","div",null,{"children":"服务端组件"}]}]}]

如上结构将解析成:

<div class="main">
  <ClientCpn>
    <div>服务端组件div>
  ClientCpn>
div>

更多请查阅 /a/119000004…

2. #请求 减轻请求瀑布流问题

network waterfalls from https://blog.sentry.io/fetch-waterfall-in-react/

waterfall 问题并不会消失,但是通过将其从客户端挪到移动端,我们能获得更多收益。

服务端的网络情况相对客户端更可控和更稳定。

更可控:如果你的服务器运行缓慢,你可以解决这个问题,你可以让服务器变得更快,比如我们利用现代基础设施,并减少通过网络传输的数据量,但你无法控制用户的网络。

更稳定:服务端的网络情况相对而言,其 RRT 和稳定性都能得到更好的保障。

针对可以并行获取的场景,服务端并发请求要比客户端快很多。

针对无法并行获取的场景,同理通过将数据获取放到服务端,虽然串行但是仍能在一次网络请求中获得所有数据。

其次,通过 RSC 的 stream 流式传输结合 可以减少 TTFB 事件即让用户尽快看到页面而非白屏,减少白屏时间。

预请求:针对组件嵌套但是没有强依赖的情况,支持 RSC 的框架给开发者提供预请求 API,速度也能接近并行。

注意 waterfall 并没有完全解决,只是我们有了更多办法去处理它而且效果是明显要更好的。

3. #请求 零网络流量 0 Network Traffic

因为无请求,服务端直接对接 db 或者其他微服务,没有 API 网络流量消耗

4. #请求 无需暴露 API 接口 0 Public API

无需精心设计 Public API。因为数据获取直接在服务端,无需暴露 API 接口。

5. #请求 零 API 对接联调时间

无 API 无联调。

6. #请求 按需获取数据,随时裁剪

比如之前设计了一个获取多个模块数据的 API,当某个模块需要删除,我们需要让服务端接口删除对应的返回值,如果是 RSC 我们可以直接在服务端删除该组件,其内部获取数据的 sql 语句也会被一并删除。可维护性更好。

7. #静态资源 自动分包 Automatic Code Splitting

RSC 将所有客户端组件当做潜在分包点,并且可以让开发者决定下载优先级。开发者不必费心在用 lazy + 动态 import,RSC 自动帮我们做了而且能比我们做得更好,动态 import 会推迟加载时间反而抵消了分包的一些优势。

8. 代码共享

客户端和服务端组件在同一个代码库,使用同一种语言,可以共享组件、业务逻辑、utils 或者类型文件。

这是 Ruby / PHP / JSP 所不能做到的。

劣势分析1. 交互组件无法适用

因为运行在服务端,state 以及 lifecycle 相关的 hooks、浏览器 API、事件处理等都不能使用。

2. 对 SEO 不友好 ️

因为 RSC 输出的是流式的协议数据,而非 HTML 片段,如果需要 SEO 友好则要结合 SSR。

FAQRSC 是编译时还是运行时?

答:编译+运行。第一层编译(webpack)会将只会使用一次的笨重的依赖移除(本文中的 moment、marked 等),最后输出编译后的数据流是每次请求都会重新从服务端获取(这就是所谓的运行时)。SSG(Static Site Generation)才是真正的编译时。

比如,每次运行都会执行判断是否今天,故 dayjs 无法在编译时去除。

import dayjs from 'dayjs'
export default function Header() {
    const time = dayjs(note.time).isToday() ? dayjs(note.time).format('YYYY-MM-DD HH:mm:ss') : dayjs(note.time).format('HH:mm:ss')
    return (
      <header>
        <div>{ time }div>
      header>
    );
}

RSC 和 SSR 是一回事吗?

答:最大区别 SSR 输出的是 HTML,RSC 输出的是数据流,客户端负责拼装为 HTML。二者可以一起使用。

参考


上一条查看详情 +衍生于OpenGLES2.0,可以
下一条 查看详情 +没有了