51工具盒子

依楼听风雨
笑看云卷云舒,淡观潮起潮落

CSS-in-JS 和 React 服务器组件:开发人员指南

CSS-in-JS 和 React 服务器组件:开发人员指南

随着React 生态系统的不断发展,尤其是Next.js 转向"服务器优先"方法,使用 CSS-in-JS 解决方案(如Emotion和styled-components)的应用程序面临着重大挑战。这些 CSS-in-JS 库本质上与新的 React 范式不兼容。

虽然有人可能会认为这些库可以更新以适应新的服务器优先方向,但重要的是要记住它们是由无偿开源贡献者维护的。期望他们彻底改造他们的库以适应 React 的变化是一项艰巨且可能不切实际的任务。

让我们看看为什么一些 CSS-in-JS 解决方案无法在新的服务器优先世界中发挥作用,然后我将向您介绍一种替代方案Linaria,它使用与 styled-components 几乎相同的 API。

RSC范式

这将变成一个冗长的解释,但了解 React 过去的工作方式(React Server Components 之前)以及现在的工作方式(RSC 之后)非常重要。

Pre-RSC:服务器和客户端

那么让我们从头开始。在 RSC 之前,React 在服务器和客户端上都渲染了一个"组件"。React始终在服务器上渲染 HTML 页面的静态部分,并将其发送到客户端进行"hydrated"。hydrated 阶段是客户端 JavaScript,负责处理 refs 或附加事件处理程序,这通常允许交互。

如果觉得两次执行这项工作有点愚蠢 --- 一次在服务器上,然后在客户端上再次重播 --- 那是因为它确实有点愚蠢。我之前曾通过比较React 的 hydration 与 Qwik 的可恢复方法讨论过这个问题,对我来说这更有意义。

RSC 后:仅限服务器

然而,在后 RSC 世界中,默认情况下所有 React 组件都是服务器专用的。React 会渲染 HTML 页面的静态部分,但不会在客户端上对其进行水合。这减少/消除了任何客户端 JavaScript。这是一种有趣的方法,因为在后 RSC 世界中,我们可以从任何组件、树中的任何级别发出服务器端请求,而不仅仅是在路由级别,这是 Next.js 以前的工作方式。

React 的新使用客户端指令

在后 RSC 世界中,您仍然可以启用客户端 JavaScript 和 React 特定方法(例如useStateuseEffect等),但您必须明确将use client指令添加到文件顶部,以便 React 能够以与以往相同的方式运行。

为什么 CSS-in-JS 不能与 RSC 一起使用?

为了增加一些背景信息,重要的是要了解传统的 CSS-in-JS 库是如何工作的,以便您能够理解为什么它们在后 RSC 世界中无法工作。

诸如 Emotion 和 styled-components 之类的库在运行时使用客户端 JavaScript 完成了很多繁重的工作,而我们已经确定 RSC 或"仅限服务器"的组件中不再存在这些 JavaScript。

你可能已经看到过这种效果。如果你检查任何使用 styled-components 的应用程序或网站,你会注意到那些看起来很奇怪的类名。

<html>    
<body>    
<a class="sc-4c0ad8fd-0 hcJJXU">    
styled link    
</a>    
</body>    
</html>

这是因为使用 styled-components,您(开发人员)无需编写 CSS 类,只需编写组件 --- 并且 styled-components(库)负责提取样式,将其转换为浏览器可以理解的 CSS 并生成一个随机但唯一的类名,然后将其应用于 DOM 元素以将两者链接在一起。

例如,您可以这样使用 styled-components 定义 HTML 锚元素以在 React 组件中使用。


// src/components/some-component/index.js    
import styled from 'styled-components';    
const SomeComponent = () => {    
return (    
<ButtonLink>    
styled link    
</ButtonLink>    
);    
}    
const ButtonLink = styled.a`    
  background: transparent;    
  border-radius: 3px;    
  border: 1px solid var(--accent-color);    
  color: var(--accent-color);    
  display: inline-block;    
  margin: 0.5rem 1rem;    
  padding: 0.5rem 0;    
  transition: all 200ms ease-in-out;    
  width: 11rem;    
`;    
export default SomeComponent

当 styled-components 运行时,它会创建类名(如上所示),将其添加到 DOM 元素,并将styled.a创建时定义的 CSS 值转换为浏览器可以理解的 CSS,例如:

.hcJJXU {    
background: transparent;    
border-radius: 3px;    
border: 1px solid var(--accent-color);    
color: var(--accent-color);    
display: inline-block;    
margin: 0.5rem 1rem;    
padding: 0.5rem 0px;    
transition: all 200ms ease-in-out 0s;    
width: 11rem;    
}

但正如前面提到的,这一切都发生在运行时,在客户端,使用客户端 JavaScript。

遗憾的是,由于 RSC 被设计为在服务器上呈现,然后作为 HTML 发送到客户端,它们具有特定的特性和限制 - 特别是对于客户端 JavaScript,它会阻止 styled-components 等库的工作。

一切尚未失去

针对此问题,有许多较新的解决方案,将繁重的工作从客户端转移到构建。我们在这里已经做了很多繁重的工作,将 TypeScript 转换为"浏览器 Js",因此,目前将 CSS-in-JS 用作后 RSC 世界的解决方案似乎是一个很好的解决方案。

但是,你可能会想:这是否意味着全世界的开发人员都需要styled.使用一些新奇的 API 重写所有声明?因为那将是一场噩梦!

值得庆幸的是, Linaria的创建者(实际上自2017 年起就已存在)选择使用与 styled-components 非常相似的 API。MUI团队的Pigment CSS也遵循了类似的模式,这是个好消息。

RSC 兼容 CSS-in-JS 库如何工作?

对于 Linaria 来说,它利用了CSS 模块。如果我styled.a再次使用该示例:

// src/components/some-component/index.js    
import styled from '@linaria/react';    
const SomeComponent = () => {    
return (    
<ButtonLink>    
styled link    
</ButtonLink>    
);    
}    
const ButtonLink = styled.a`    
  background: transparent;    
  border-radius: 3px;    
  border: 1px solid var(--accent-color);    
  color: var(--accent-color);    
  display: inline-block;    
  margin: 0.5rem 1rem;    
  padding: 0.5rem 0;    
  transition: all 200ms ease-in-out;    
  width: 11rem;    
`;    
export default SomeComponent

Linaria 不会将其转换为随机但唯一的类名,而是创建一个[component name].module.css文件并将样式声明转换为真正的 CSS 命名类,例如:


/* src/components/some-component/SomeComponent.module.css */    
.ButtonLink {    
background: transparent;    
border-radius: 3px;    
border: 1px solid var(--accent-color);    
color: var(--accent-color);    
display: inline-block;    
margin: 0.5rem 1rem;    
padding: 0.5rem 0px;    
transition: all 200ms ease-in-out 0s;    
width: 11rem;    
};

在应用程序的构建步骤期间,.css将向组件添加对 CSS 模块文件的导入,并将样式声明转换为底层 HTML 元素(在本例中它是一个锚元素),并添加对该类的引用:

import styles from './SomeComponent.module.css';    
const SomeComponent = () => {    
return (    
<a className={styles.ButtonLink}>    
styled link    
</a>    
);    
}    
export default SomeComponent

由于这一切都发生在构建步骤中,浏览器无需执行任何操作 --- 因此,无需客户端 JavaScript。这实际上是解决问题的一个很酷的方法,迁移所需的只是卸载 styled-components、安装 Linaria,然后执行从import styled from 'styled-components';到 的查找和替换import styled from '@linaria/react';

总结

所以,当像 React 这样广泛使用的东西彻底改变其基本工作方式时,许多为其设计的开源项目也需要改变------或者,在某些不幸的情况下,被淘汰出局。在 JavaScript 领域,一切都在频繁变化,我们所能做的就是努力跟上!

赞(1)
未经允许不得转载:工具盒子 » CSS-in-JS 和 React 服务器组件:开发人员指南