技术前端开发Netflix 删除了 react?

来源 Netflix Removed React?

Netflix 删除了 React

“Netflix 删除了 React,网站加载时间减少了 50%!”

这条爆炸性新闻在推特炸开了锅。这不禁让人想起半年前”微软应该禁止所有新项目使用 React” 的新闻 - 两大科技巨头相继 “抛弃” React,难道这预示着什么?

“React 心智负担重、性能差、bundle 体积大…”. 一时间,各种唱衰 React 的声音此起彼伏。有人甚至信誓旦旦地表示:“只要像 Netflix 一样干掉 React,你的网站性能立马提升 50%!”

fake-twitter

这条发布于 2024 年 10 月 27 日的推文有着惊人的 150 万浏览量。但是,这大概率是 AI 生成的假新闻。 事实上,我们去 Netflix 的官网打开 react-devtools,发现他们依然在使用 React 构建他们的网站。

netflix-react-devtools

Netflix 的真实案例

这篇 AI 生成的假新闻灵感来自 2017 年 Netflix 工程师在 hack news 上发布的一篇文章 - Netflix: Removing client-side React.js improved performance by 50%

他直接移除了这篇文章最重要的部分 - client-side React.js, 也就是客户端的 React.js 代码。

实际的情况是,Netflix 团队在 2017 年的时候在使用 React 构建他们的 landing page

为什么在一个简单的 landing page 上要使用 React 呢?因为在 landing page

  • Netflix 需要处理大量的 AB 测试
  • 支持近 200 个国家的本地化
  • 根据用户设备、地理位置等因素动态调整内容
  • 需要服用现有的 React 组件

基于上述需求的考虑,Netflix 团队选择了使用 React 来构建他们的 landing page

为了优化页面加载性能,他们采用了服务端渲染的方案。同时,为了保证后续交互的流畅性,系统会预先加载(pre-fetch)后续流程所需的 React/Redux 相关代码。

从架构上看,这个 landing page 本质上仍然是一个单页面应用(SPA),保持了 SPA 快速响应的优势。不同之处在于首次访问时,页面内容是由服务端直接生成的,这样可以显著提升首屏加载速度。

这样做的缺点

显然,Netflix 在 2017 年这么做是有原因的,他们当时的确也没有更好的方案。在 2025 年的今天, 再来回顾这个方案,显然有以下缺点:

数据重复获取

在首屏渲染时,服务端需要获取数据来生成 HTML,而在客户端激活(hydration)后,为了保持交互性,往往又需要重新获取一遍相同的数据。 这种重复的数据获取不仅浪费资源,还可能带来不必要的性能开销。

客户端代码体积膨胀

因为本质上,Netflixlanding page 是一个还是一个 SPA,那么不可避免的,所有可能的 UI 状态都需要打包, 即使用户只需要其中的一部分代码。例如,在一个很常见的 tabs 页面

  <Tabs
    defaultActiveKey="1"
    items={[
      {
        label: 'Tab 1',
        key: '1',
        children: 'Tab 1',
      },
      {
        label: 'Tab 2',
        key: '2',
        children: 'Tab 2',
        disabled: true,
      },
    ]}
  />

即使用户只点击了 Tab 1, 即使 Tab 2 没有被渲染,但是 Tab 2 的代码也会被打包。

如何解决这些问题

React Server Components (RSC) 为上述问题提供了优雅的解决方案:

避免数据重复获取

使用 RSC,组件可以直接在服务器端获取数据并渲染,客户端只接收最终的 HTML 结果。不再需要在客户端重新获取数据。

智能代码分割

RSC 允许我们选择性地决定哪些组件在服务器端运行,哪些在客户端运行。例如:

function TabContent({ tab }: { tab: string }) {
  // 这部分代码只在服务器端运行,不会打包到客户端
  return <div>{tab} 内容</div>
}
 
// 客户端组件
'use client'
function TabWrapper({ children }) {
  const [activeTab, setActiveTab] = useState('1')
  return (
    <div>
      {/* Tab 切换逻辑 */}
      {children}
    </div>
  )
}

在这个例子中:

  • TabContent 的所有可能状态都在服务器端预渲染
  • 只有实际需要交互的 TabWrapper 会发送到客户端
  • 用户获得了更小的 bundle 体积和更快的加载速度

这不就是 PHP?

经常会看到有人说:“Server Components 不就是重新发明了 PHP 吗?都是在服务端生成 HTML。”

显然,PHP 与现在的 Server Components 在开发体验上有本质的区别。

1. 细粒度的服务端-客户端混合

与 PHP 不同,RSC 允许我们在组件级别决定渲染位置,用一个购物车的例子来说明:

// 服务端组件
function ProductDetails({ id }: { id: string }) {
  // 在服务器端获取数据和渲染
  const product = await db.products.get(id);
  return <div>{product.name}</div>;
}
 
// 客户端组件
'use client'
function AddToCart({ productId }: { productId: string }) {
  // 在客户端处理交互
  return <button onClick={() => addToCart(productId)}>加入购物车</button>;
}
 
// 混合使用
function ProductCard({ id }: { id: string }) {
  return (
    <div>
      <ProductDetails id={id} />
      <AddToCart productId={id} />
    </div>
  );
}

这种设计充分利用了服务端和客户端各自的优势 - 在服务端可以直接访问数据库获取 ProductDetails 所需的数据,而在客户端则能更好地处理 AddToCart 这样的用户交互。这不仅提升了性能,也让代码结构更加清晰合理。

2. 保持组件的可复用性

RSC 最强大的特性之一是组件的可复用性不受渲染位置的影响:

// 这个组件可以在服务端渲染
function UserProfile({ id }: { id: string }) {
  return <ProfileCard id={id} />;
}
 
// 同样的组件也可以在客户端动态加载
'use client'
function UserList() {
  const [selectedId, setSelectedId] = useState(null);
  return selectedId ? <ProfileCard id={selectedId} /> : null;
}

因为都是 React 组件,区别仅仅是渲染位置的不同,同一个组件可以:

  • 在服务端预渲染时使用
  • 在客户端动态加载时使用
  • 在流式渲染中使用

这种统一的组件模型是 PHP 等传统服务端渲染所不具备的。

3. 智能的序列化

RSC 还提供了智能的序列化机制,可以自动将组件的 propsstate 序列化,从而在服务端和客户端之间传递。 避免了重复获取数据的问题。

// 服务端组件
async function Comments({ postId }: { postId: string }) {
  // 1. 获取评论数据
  const comments = await db.comments.list(postId);
  
  // 2. 传递给客户端组件
  return <CommentList initialComments={comments} />;
}
 
// 客户端组件
'use client'
function CommentList({ initialComments }) {
  // 3. 直接使用服务端数据,无需重新请求
  const [comments, setComments] = useState(initialComments);
  
  return (
    // 渲染评论列表
  );
}

4. 渐进式增强

RSC 还提供了渐进式增强的能力,可以在服务端和客户端之间无缝过渡。

  • 首次访问时返回完整的 HTML
  • 按需加载客户端交互代码
  • 保持应用的可访问性

这让我们能够构建既有良好首屏体验,又能提供丰富交互的现代应用,完美解决了在 2017 年 Netflix 所提出的问题。

总结

通过对上面这些案例的分析,我们可以看出

1. 不要轻信网络传言

网络上充斥着各种技术传言。虽然像上面这种完全虚构的假新闻容易识破,但有些传言会巧妙地利用真实数据和案例,通过夸张的描述来误导技术选型和决策,这类信息需要我们格外谨慎分辨。 例如:

svelte 放弃 TypeScript 改用 JSdoc 进行类型检查

这个确实是一个真的新闻,但是并不代表着 Typescript 的没落,实际上

  • Svelte 团队选择 JSDoc 是为了减少编译时间
  • 这是针对框架源码的优化,而不是面向使用者的建议
  • Svelte 依然完整支持 TypeScript,用户代码可以继续使用 .ts/.ts

tauri 打包后的体积比 electron 小多了,我们应该放弃 electron 使用 tauri

技术选型不能仅仅看单一指标。虽然 tauri 的打包体积确实小于 electron,但在开发体验、性能、稳定性、生态和社区支持等关键维度上都存在明显短板。

如果你尝试用 tauri 开发复杂应用,很可能会因为生态不完善、社区支持不足而陷入困境。当你遇到问题去 GitHub 寻找解决方案时,看到许多库已经一年未更新,就会明白为什么大多数团队仍在选择 electron

2. 历史的选择

2017 年的 Netflix 面临着复杂的业务需求,他们选择了当时最佳的解决方案 - 服务端渲染 + 客户端激活。这个方案虽然解决了问题,但也带来了一些困扰:

  • 数据需要在服务端和客户端重复获取
  • JavaScript bundle 体积过大

3. RSC 带来的改变

React Server Components 为这些历史遗留问题带来了全新的解决思路:

  • 服务端渲染与客户端渲染完美融合
  • 智能的代码分割,最小化客户端 bundle 体积
  • 数据获取更高效,避免重复请求
  • 渐进式增强,提供流畅的用户体验

4. 技术演进的启示

Netflix 2017 年的实践到今天的 RSC,我们可以看到:

  • 技术方案在不断进化,过去的最佳实践可能已不再适用
  • RSC 不是简单的”回归服务端”,而是开创了全新的开发模式
  • 性能与开发体验不再是非此即彼的选择

RSC 代表了现代前端开发的新趋势 - 既保持了 React 强大的组件化能力,又通过创新的架构设计解决了历史难题。这让我们终于可以在性能和开发体验之间找到完美平衡。