介绍
去年的这个时候,Vercel 宣布了 Next 13.4 的稳定版本发布,成为首个构建在 React Server Component 之上的 React 框架。
这是一件大事!RSC(React Server Component)为我们提供了一种在 React 中编写 仅限服务器的代码 的官方方式。它打开了许多有趣的新可能。
但是不能丢掉一些蛋就做不出煎蛋了。RSC 是 React 工作方式的根本变化,我们一直在使用的一些库和工具已经变得混乱不堪。 😅
令人担忧的是,最受欢迎的 CSS-in-JS 库,如 💅 styled-components 和 Emotion,与 React 的这一新视觉并不完全兼容,而且目前尚没有明确的前进方向。
在过去的几个月里,我一直在研究这个问题,了解兼容性问题,并了解可行的解决方案。目前,我感觉对整个情况有了相当扎实的把握。我还发现了一些潜在的令人兴奋的进展。 ✨
如果你使用 CSS-in-JS 库,我希望这篇博文能帮助消除许多困惑,并为你提供一些实用的选择。
如果你 不 使用 CSS-in-JS 库,这篇博文也应该有助于加深你对 React Server Component 的理解。我们将在这里讨论的许多问题并不特定于 CSS-in-JS!
**只需使用 **
在线讨论时,其中最常见的建议之一是切换到不同的 CSS 工具。毕竟,在 React 生态系统中有数不尽的选择!
然而,对于我们许多人来说,这并不是一个切实可行的建议。我的博客和课程平台上有超过 5,000 个样式化组件。迁移到完全不同的工具比说起来要容易得多。
而且,即使我 能够 一挥手,换上完全不同的库,我也不想这么做。我真的很喜欢
styled
API!在本博文的后面,我们将讨论一些替代 CSS 库,但我们将重点关注与 styled-components 类似的 API 的选项。
解析 React Server Component
为了理解兼容性问题,我们需要了解 React Server Component。不过,在谈论这个之前,我们需要确保理解 服务器端渲染(SSR)。
SSR 是一个涵盖多种不同策略和实现的总称,但最典型的版本如下:
- 用户访问我们的 Web 应用程序。
- 请求被 Node.js 接收,它在无窗口的服务器环境中运行 React。它渲染我们的应用程序并生成一个包含所有初始 UI 的完整 HTML 文档。
- 当这个 HTML 文档在用户设备上加载时,React 将重新渲染所有相同的组件,重复服务器上执行的工作。然而,它不会生成 新的 HTML 元素,而是“采用”服务器生成的 HTML 元素。这被称为 水合。
React 需要在用户设备上运行以处理交互。服务器生成的 HTML 是完全静态的;它不会包含我们编写的任何事件处理程序(例如 onClick
),也不会捕获我们指定的任何引用(使用 ref
属性)。
好了,但为什么它必须重新做完全相同的工作?? 当 React 在用户设备上启动时,它会发现一堆预先存在的 UI,但它不会对此有任何 上下文,比如哪个组件拥有每个 HTML 元素。React 需要执行完全相同的工作来重建组件树,以便能够正确地连接现有的 HTML,将事件处理程序和引用附加到正确的元素上。React 需要绘制自己的地图,以便能够继续服务器离开的地方。
这个模型有一个重要的限制。 我们编写的所有代码都将在服务器上 和 客户端上执行。没有办法创建仅在服务器上渲染的组件。
假设我们正在构建一个具有数据库中数据的全栈 Web 应用程序。如果你是从像 PHP 这样的语言过来,你可能期望能够做类似这样的事情:
function Home() {
const data = db.query('SELECT * FROM SNEAKERS');
return (
<main>
{data.map(item => (
<Sneaker key={item.id} item={item} />
))}
</main>
);
}
理论上,这段代码在服务器上可能完全正常工作,但是 完全相同的代码 将在用户设备上重新执行,这是个问题,因为客户端的 React 没有访问我们的数据库。没有办法告诉 React “只在服务器上运行此代码,并在客户端重用生成的数据”。
建立在 React 之上的元框架已经提出了自己的解决方案。例如,在 Next.js 中,我们可以这样做:
export async function getServerSideProps() {
const data = await db.query('SELECT * FROM SNE
AKERS');
return {
props: {
data,
},
};
}
function Home({ data }) {
return (
<main>
{data.map(item => (
<Sneaker key={item.id} item={item} />
))}
</main>
);
}
Next.js 团队说“好吧,所以完全相同的 React 代码必须在服务器和客户端上运行… 但是我们可以添加一些 额外 的代码,在 React 之外,仅在服务器上运行!”。
当 Next.js 服务器收到请求时,它将首先调用 getServerSideProps
函数,它返回的任何内容都将作为 props 提供给 React 代码。完全相同的 React 代码在服务器和客户端上运行,所以没有问题。相当聪明,对吧?
老实说,我今天仍然是这种方法的忠实拥护者。但它确实感觉有点像一种变通方法,一个由于 React 的限制而产生的 API。它也仅在 页面 级别、在每个路由的顶部才起作用;我们不能随意插入 getServerSideProps
函数。
React Server Component 为此问题提供了更直观的解决方案。 使用 RSC,我们可以在我们的 React 组件中直接执行数据库调用和其他仅限服务器的工作:
async function Home() {
const data = await db.query('SELECT * FROM SNEAKERS');
return (
<main>
{data.map(item => (
<Sneaker key={item.id} item={item} />
))}
</main>
);
}
在“React Server Component”范式中,默认情况下,组件是 Server Component。Server Component 仅在服务器上运行。这段代码将 不 会在用户设备上重新运行;该代码甚至不会包含在 JavaScript 包中!
这种新的范式还包括 Client Component。Client Component 是在 服务器 和 客户端 上运行的组件。你以前在“传统”(RSC 前)React 中编写的每个 React 组件都是 Client Component。这是一个旧东西的新名称。
我们通过文件顶部的新 "use client"
指令选择 Client Component:
'use client';
function Counter() {
const [count, setCount] = React.useState(0);
return (
<button onClick={() => setCount(count + 1)}>
Count: {count}
</button>
);
}
该指令创建了一个“客户端边界”;该文件中的所有组件,以及任何导入的组件,都将呈现为 Client Component,在服务器上首先运行,然后在客户端运行。
与其他 React 功能(例如钩子)不同,React Server Component 需要与捆绑器进行深度集成。截至我撰写本文的 2024 年 4 月,使用 React Server Component 的唯一实用方法是使用 Next.js,尽管我预计将来会发生变化。
Server Component 的局限性
理解 Server Component 的关键是它们不提供“完整”的 React 体验。大多数 React API 在 Server Component 中不起作用。
例如,useState
。当状态变量更改时,组件会重新渲染,但 Server Component 无法 重新渲染;它们的代码甚至从未发送到浏览器,因此 React 将不知道如何处理状态更改。从 React 的角度来看,Server Component 生成的任何标记都是固定的,不能在客户端上更改。
同样,我们不能在 Server Component 内部使用 useEffect
,因为效果不会在服务器上运行,它们只会在客户端渲染后运行。由于 Server Component 被排除在我们的 JavaScript 包之外,客户端的 React 将永远不会知道我们编写了 useEffect
钩子。
甚至 useContext
钩子也不能在 Server Component 内部使用,因为 React 团队尚未解决如何在 Server Component 和 Client Component 之间共享 React Context 的问题。
我是这样看待它的: Server Component 不是 真正的 React 组件,至少不是我们传统理解的那样。它们更像是 PHP 模板,由服务器渲染以创建原始 HTML。真正的创新在于 Server Component 和 Client Component 可以在同一个应用程序中共存!
更深入的探索
在这篇博文中,我想重点关注 React Server Component 的最重要细节,以便了解与 CSS-in-JS 框架的兼容性问题。
但如果你想更深入地了解 React Server Component,我还有一篇单独的博文,更深入地探讨了这个新世界:
CSS-in-JS 库的工作原理
好了,我们已经介绍了 React Server Component 的基础知识。现在让我们谈谈 CSS-in-JS 库如 💅 styled-components 的基础知识!
这里有一个快速示例:
import styled from 'styled-components';
export default function Homepage() {
return (
<BigRedButton>
Click me!
</BigRedButton>
);
}
const BigRedButton = styled.button`
font-size: 2rem;
color: red;
`;
我们不是将 CSS 放在类似 .red-btn
的类中,而是将 CSS 附加到新生成的 React 组件。这就是 styled-components 特殊的地方;组件 是可重用的原始
,而不是类。
styled.button
是一个动态生成新 React 组件的函数,我们将该组件分配给一个名为 BigRedButton
的变量。然后我们可以像使用任何其他 React 组件一样使用该 React 组件。它将渲染一个具有大红文本的 <button>
标签。
但是这个库究竟是如何将此 CSS 应用于此元素的呢?我们有三个主要选项:
- 样式可以通过
style
属性内联应用。 - 样式可以放在单独的 CSS 文件中,并通过
<link>
加载。 - 样式可以放在
<style>
标签中,通常在当前 HTML 文档的<head>
中。
如果我们运行此代码并检查 DOM,答案就揭晓了:
<html>
<head>
<style data-styled="active">
.abc123 {
font-size: 2rem;
color: red;
}
</style>
</head>
<body>
<button className="abc123">
Click me!
</button>
</body>
</html>
styled-components 将所提供的样式写入由库管理的 <style>
标签中。为了将这些样式与此特定的 <button>
相连接,它生成了一个唯一的类名,"abc123"
。
所有这些工作首先发生在初始 React 渲染期间:
- 在客户端渲染环境中(例如 Parcel、create-react-app),
<style>
标签是在用户设备上动态生成的,就像 React 创建的所有 DOM 节点一样。 - 在服务器端渲染环境中(例如 Next、Remix),这项工作在服务器上进行。生成的 HTML 文档将包含此
<style>
标签。
随着用户与我们的应用程序进行交互,某些样式可能需要创建、修改或销毁。例如,假设我们有一个有条件渲染的 styled-components:
function Header() {
const user = useUser();
return (
<>
{user && (
<SignOutButton onClick={user.signOut}>
Sign Out
</SignOutButton>
)}
</>
);
}
const SignOutButton = styled.button`
color: white;
background: red;
`;
最初,如果 user
未定义,<SignOutButton>
将不会被渲染,因此这些样式将不存在。稍后,如果用户登录,我们的应用程序将重新渲染,并且 styled-component 将启动,将这些样式注入到 <style>
标签中。
基本上,每个 styled 组件都是一个常规的 React 组件,但多了一个额外的小副作用:它们还将它们的样式渲染到 <style>
标签中。
对于我们今天的目的,这是最重要的收获,但如果你想深入了解库的内部工作原理,我写了一篇关于它的博文,名为 “解密 styled-components”。
问题的关键
总结我们到目前为止学到的内容:
- “React Server Component”是 React 的一个新范式,为我们提供了一种新类型的组件,即 Server Component。Server Component 仅在服务器上渲染。它们的代码甚至不包含在我们的 JS 包中。
- styled-components 库允许我们动态创建附带 CSS 的 React 组件。它通过管理一个
<style>
标签来工作,该标签在组件重新渲染时会更新。
根本的不兼容性在于,styled-components 设计为在浏览器中运行,而 Server Component 从不接触浏览器。
在内部,styled-components 大量使用了 useContext
钩子。它旨在与 React 生命周期绑定,但 Server Component 没有 React 生命周期。因此,如果我们想在这个新的“React Server Component”世界中使用 styled-components,每个渲染 至少一个 styled-components 的 React 组件都需要成为 Client Component。
我不知道你怎么想,但对我来说,很少有 React 组件 不 包含任何样式。我估计我的组件文件中有超过 90% 使用 styled-components。这些组件中的大多数否则完全是静态的;它们不使用状态或任何其他 Client Component 功能。
这有点让人沮丧,因为这意味着我们无法充分利用这种新的范式…… 但事实上这并不是世界末日。
如果我能改变 React Server Component 的一件事,那就是“Client Component”这个名称。这个名称暗示这些组件 只 在客户端渲染,但事实并非如此。记住,“Client Component”是一个旧东西的新名称。每个 React 组件,在 2023 年 5 月之前创建的每个 React 应用程序中,都是 Client Component。
因此,即使在 styled-components 应用程序中,只有 10% 的组件可以成为 Server Component,这仍然是一个改进! 我们的应用程序仍然比 RSC 之前的世界轻量级和快速。我们仍然获得所有 SSR 的好处。这一点没有改变。
他们为什么不更新呢?
你可能想知道为什么 styled-components / Emotion 的维护者还没有更新他们的库以与 React Server Component 兼容。我们已经知道这个问题已经存在了一年多了,为什么他们还没有找到解决方案??
styled-components 的维护者目前受到 React 中缺失的 API 的阻碍。具体来说,React 还没有为 RSC 提供与 Context 兼容的替代方案,而 styled-components 需要 某种 方法在组件之间共享数据,以便在服务器端渲染期间正确应用所有样式。
几周前,我进行了 相当深入的探索,老实说,我很难想象在没有 React Context 的情况下如何使其正常工作。据我所知,唯一的解决方案是完全重写整个库以使用完全不同的方法。这不仅会导致重大的破坏性变化,而且也是对一个由志愿者开源维护者团队要求的完全不合理的事情。
如果你想了解更多信息,可以查看一个 styled-components 的 Github 问题,其中解释了阻碍的原因。我在 Emotion 存储库中也看到了类似的讨论。
无运行时 CSS-in-JS 库的世界
到目前为止,故事有点沉重。React Server Component 与 styled-components 之间 存在 根本性的不兼容性,而库维护者并没有获得他们需要的工具来添加支持。
幸运的是,React 社区并没有对这个问题置之不理! 有几个正在开发的库提供了类似于 styled-components 的 API,但与 React Server Component 完全兼容!✨
这些工具不是绑定到 React 生命周期中,而是采取了不同的方法;所有处理都在 编译时 进行。
现代 React 应用程序有一个构建步骤,在这一步中,我们将 TypeScript/JSX 转换为 JavaScript,并将成千上万个单独的文件打包成少数几个捆绑包。这项工作在我们的应用程序部署时完成,在它开始在生产环境中运行之前。为什么不在此步骤中处理我们的 styled 组件,而不是在运行时呢?
这是我们将在本节讨论的所有库的核心思想。让我们深入研究一下!
Linaria
Linaria 早在 2017 年 就创建了。它几乎和 styled-components 一样老!
API 看起来与 styled-components 完全相同:
import styled from '@linaria/react';
export default function Homepage() {
return (
<BigRedButton>
Click me!
</BigRedButton>
);
}
const BigRedButton = styled.button`
font-size: 2rem;
color: red;
`;
这里真正聪明的部分是: 在编译步骤中,Linaria 转换此代码,并将所有样式移入CSS 模块。
运行 Linaria 后,代码将类似于以下内容:
/* /components/Home.module.css */
.BigRedButton {
font-size: 2rem;
color: red;
}
/* /components/Home.js */
import styles from './Home.module.css';
export default function Homepage() {
return (
<button className={styles.BigRedButton}>
Click me!
</button>
);
}
如果你还不熟悉 CSS 模块,它是 CSS 的一个轻量级抽象。你几乎可以将其视为普通 CSS,但你不必担心全局唯一名称。在编译步骤中,就在 Linaria 运行其魔法之后,类似 .BigRedButton
的通用名称会被转换为像 .abc123
这样的唯一名称。
重要的是,CSS 模块已经得到了广泛的支持。 它是最受欢迎的选项之一。像 Next.js 这样的元框架已经对 CSS 模块提供了一流的支持。
因此,与其重新发明轮子并花费多年时间构建一个强大的生产就绪 CSS 解决方案,Linaria 团队决定走捷径。我们可以编写 styled-components,而 Linaria 将预处理它们为 CSS 模块,然后将其处理为纯 CSS。所有这些都发生在编译时。
运行时与编译时的权衡
在 RSC 出现之前很久,社区就已经在构建像 Linaria 这样的编译时库了。性能优势是令人信服的:styled-components 将 11 千字节(gzip)添加到我们的 JavaScript 捆绑包中,但 Linaria 添加的是 0kb,因为所有的工作都是提前完成的。此外,服务器端渲染会快一点,因为我们不需要花时间收集和应用样式。
尽管如此,styled-components 运行时并不是无用的。我们可以在 styled-components 中做一些在编译时无法做到的事情。例如,当某些 React 状态发生变化时,styled-components 可以动态更新 CSS。
幸运的是,自从 styled-components 第一次创建以来,CSS 已经变得更加强大。我们可以使用 CSS 变量来处理大多数动态用例。如今,运行时在某些情况下可能会提供稍微更好的开发人员体验,但在我看来,它实际上并不是必要的。
这 确实 意味着 Linaria 和其他编译时 CSS-in-JS 库不会真正成为 styled-components/Emotion 的即插即用替代品。我们将不得不花费一些时间重新设计动态组件。但与切换到完全不同的 CSS 工具相比,这只是工作的一小部分。
迁移到 Linaria
那么,我们应该将我们的 styled-components 应用程序都迁移到 Linaria 吗?
不幸的是,存在一个问题。虽然 Linaria 本身正在积极维护,但没有官方的 Next.js 绑定,而且让 Linaria 与 Next.js 一起工作并不容易。
最流行的集成方案,next-linaria,已经有 3 年没有更新了,并且不兼容 App Router / React Server Component。还有一个更新的选项,next-with-linaria,但是它有一个关于不在生产环境中使用的大警告。😅
因此,尽管这可能是一个适合冒险者的选择,但我并不认为这是我可以推荐的东
西。
Panda CSS
Panda CSS 是由构建了流行组件库 Chakra UI 的开发人员开发的现代 CSS-in-JS 库。
Panda CSS 有许多不同的接口。你可以像 Tailwind 一样使用它,指定简写类名,如 mb-5
。你可以像 Stitches 一样使用它,使用变体和 cva。或者,你可以像 styled-components 一样使用它。
这是使用 styled
API 的样子:
import { styled } from '../styled-system/jsx'
export default function Homepage() {
return (
<BigRedButton>
Click me!
</BigRedButton>
);
}
const BigRedButton = styled.button`
font-size: 2rem;
color: red;
`;
与 Linaria 类似,Panda CSS 也会编译成 Tailwind 风格的实用类. 最终结果将类似于以下内容:
/* /styles.css */
.font-size_2rem {
font-size: 2rem;
}
.color_red {
color: red;
}
/* /components/Home.js */
export default function Homepage() {
return (
<button className="font-size_2rem color_red">
Click me!
</button>
);
}
对于像 color: red
这样的每个唯一的 CSS 声明,Panda CSS 将在一个中央 CSS 文件中创建一个新的实用类。然后该文件将在我们 React 应用程序的每个路由上加载。
我真的很想喜欢 Panda CSS。它由一支经验丰富的团队开发,提供了一个熟悉的 API,甚至还有一个可爱的滑板和珍珠奶茶的熊猫吉祥物!
然而,在尝试过后,我发现它不适合我。我的一些问题是琐碎的/肤浅的;例如,Panda CSS 生成了一堆 东西,这些东西使项目文件变得混乱。这对我来说有点乱,但最终并不是一个重大问题。
对我来说,更大的问题是 Panda CSS 缺少一个关键功能。我们无法交叉引用组件。
通过一个例子来解释会更容易理解。在这个博客上,我有一个 TextLink
组件,它是 Next.js 的 Link
组件的一个样式包装器。默认情况下,它看起来像这样:
然而,相同的组件具有某些 上下文 样式。例如,当 TextLink
在 Aside
中时,它看起来像这样:
我使用 Aside
组件来显示辅助 / 额外信息。我发现默认的 TextLink
样式在这种情况下不太适用,因此我想应用一些覆盖样式。
这是我们如何在 styled-components 中表达这种关系的:
import Link from 'next/link';
import { AsideWrapper } from '@/components/Aside';
const TextLink = styled(Link)`
color: var(--color-primary);
text-decoration: none;
${AsideWrapper} & {
color: inherit;
text-decoration: underline;
}
`;
&
字符最近作为官方嵌套语法的一部分添加到 CSS 语言中,但多年来一直是 CSS 预处理器和工具的一个惯例。在 styled-components 中,它求值为当前选择器。
在渲染此代码时,生成的 CSS 将类似于以下内容:
.textlink_abc123 {
color: var(--color-primary);
text-decoration: none;
}
.aside_def456 .textlink_abc123 {
color: inherit;
text-decoration: underline;
}
当我使用 CSS 时,我尽量遵循一个规则:特定组件的所有样式都应该写在一个地方。 我不应该在整个应用程序中进行寻宝,以找到可能适用于给定元素的所有不同 CSS!
这就是 Tailwind 如此强大的地方之一;所有样式都放在元素本身上。我们不必担心其他组件“伸手进来”并为它不拥有的元素添加样式。
这种模式就像该思想的加强版。 我们不仅列出了所有默认情况下适用于 TextLink
的样式,还列出了上下文适用的样式。它们都在一个地方,用反引号括起来。
遗憾的是,这种模式在 Panda CSS 中不起作用。在 Panda CSS 中,我们唯一标识的是 CSS 声明,而不是元素本身,因此无法表达这些类型的关系。
如果你对这种模式不感兴趣,那么 Panda CSS 可能是你的应用程序的一个好选择!但对我来说,这是一个不可接受的问题。
styled-components 的快乐之路
如果你想了解更多关于这种“上下文样式”模式
的信息,我在我的博客文章 “styled-components 的快乐之路” 中详细介绍了它。这是我在多年的 styled-components 使用中学到的模式和技巧的集合。
Pigment CSS
最受欢迎的 React 组件库之一,Material UI,是建立在 Emotion 之上的。他们的开发团队一直在处理与 RSC 兼容性相关的所有问题,他们决定采取一些行动。
他们最近开源了一个新库。它叫做 Pigment CSS。其 API 目前应该看起来非常熟悉:
import { styled } from '@pigment-css/react';
export default function Homepage() {
return (
<BigRedButton>
Click me!
</BigRedButton>
);
}
const BigRedButton = styled.button`
font-size: 2rem;
color: red;
`;
Pigment CSS 在编译时运行,并且使用与 Linaria 相同的策略,编译为 CSS 模块。Next.js 和 Vite 都有插件。
事实上,它使用了一个叫做 WyW-in-JS(“What you Want in JS”)的低级工具。这个工具是从 Linaria 代码库演变而来的,它隔离了“编译为 CSS 模块”的业务逻辑,并使其通用化,以便像 Pigment CSS 这样的库可以在其之上构建自己的 API。
老实说,这对我来说感觉就是完美的解决方案。CSS 模块已经经过了如此多的测试和优化。从迄今为止我所看到的情况来看,Pigment CSS 很棒,性能和开发体验都很好。
Material UI 的下一个主要版本将支持 Pigment CSS,计划最终放弃对 Emotion/styled-components 的支持。因此,Pigment CSS 很可能成为最广泛使用的 CSS-in-JS 库之一。Material UI 每周在 NPM 上下载量约为 React 的 ~500 万次,大约是 React 本身的 1/5!
现在仍然很早;Pigment CSS 几周前才开源。但是团队正在为这个项目投入大量资源。我迫不及待地想看到事情的发展!
列表还在继续
除了我们到目前为止所涵盖的库之外,生态系统中还有许多其他正在做有趣事情的项目。以下是我正在关注的一些其他项目:
- next-yak — 由瑞士最大的电子商务零售商的开发人员创建,next-yak 是一个编译时 CSS-in-JS 库,旨在尽可能接近于 styled-components 的即插即用替代品,重新实现了许多其次 API。
- Kuma UI — 这个库正在尝试一种相当雄心勃勃的“混合”设计,其中大多数样式在编译时提取,但同时还为 Client Component 提供了运行时。
- Parcel macros — Parcel 是一个捆绑器,最近实现了“宏”,这是一个可以用来构建各种东西的工具,包括编译时 CSS-in-JS 库。令人惊讶的是,这个功能并不是特定于 Parcel 的,可以在 Next.js 中使用!
前进的道路
好了,我们已经探讨了大量的选项,但问题仍然存在:如果你有一个使用“传统”CSS-in-JS库的生产应用程序,你到底应该怎么做?
有点违反直觉,但在许多情况下,我实际上认为你并不需要做任何事情。 😅
很多在线讨论使人感觉好像你 不能 在现代 React / Next.js 应用程序中使用 styled-components,或者这样做会有很大的性能损失。但事实并非如此。
很多人混淆了 RSC(React Server Components)和 SSR(服务器端渲染)。服务器端渲染仍然完全按照以往的方式工作,它不受任何这些问题的影响。如果你迁移到 Next 的 App Router 或另一个 RSC 实现,你的应用程序不应该变慢。事实上,它可能会变得更快!
从性能的角度来看,RSC 和零运行时 CSS 库的主要好处是 TTI,“交互时间”。这是 UI 展示给用户和 UI 完全可交互之间的延迟。如果忽视它,可能会产生糟糕的用户体验;人们会开始点击期望它们能起作用,但什么都不会发生,因为应用程序仍在水化的过程中。
因此,如果你的应用程序现在需要很长时间才能水化,迁移到零运行时 CSS 库可能会对用户体验产生积极影响。但如果你的应用程序已经有了一个可靠的 TTI,你的用户可能不会从这次迁移中获益。
我觉得在许多情况下,最大的问题是FOMO(失去机会恐惧症)。 作为开发者,我们想要使用最新最好的工具。知道我们没有从新的优化中获得太多好处,添加一堆 "use client"
指令并不好玩。但这真的是进行大规模迁移的一个引人注目的原因吗?
我正在做什么
我维护两个主要的生产应用程序:这个博客,以及我用于交互式课程的课程平台(JavaScript 开发者的 CSS 和 React 的乐趣)。
我的课程平台仍然使用 Next.js Pages Router 与 styled-components,并且我暂时没有计划迁移它。我对它提供的用户体验感到满意,我认为迁移可能不会带来显著的性能优势。
我的博客目前也是使用 Next.js Pages Router 与 styled-components,尽管我正在将其迁移到使用 Next.js App Router。我选择使用 Linaria + next-with-linaria,至少目前是这样。当 Pigment CSS 更成熟一点时,我计划切换过去。
React Server Components 确实 非常酷。 React/Vercel 团队在重新思考 React 在服务器上的工作方式方面做得非常出色。但老实说,尽管已经标记为“稳定”,App Router 仍然远没有 Pages Router 成熟,还存在一些问题。
如果你对应用程序的性能感到满意,我认为你不应该感到任何更新/迁移的紧迫性 ❤️。你目前的技术栈将继续正常工作,几年后,你可以回来看看情况如何。