发布于 2026-01-06 2 阅读
0

为什么我们一直在讨论 CSS-in-JS?

为什么我们一直在讨论 CSS-in-JS?

你还记得第一次编写 HTML/CSS 代码时的感受吗?是不是感觉很棒?

<div style="color: blue">Hello world</div>

如此简单,如此纯粹。

当然,高级开发人员™会告诉你不要这样编写 CSS,而是把它放在一个单独的文件中。

<div class="blue-text">Hello world</div>
/* style.css */
.blue-text {
  color: blue;
}

随着你构建的元素和页面越来越多,你的style.css文件会变得越来越长,所以你需要将其拆分成多个文件。

/* landing-page.css */
.heading {
  font-size: 64px;
}
button {
  background: orange;
}
/* forms.css */
button {
  background: blue;
}

很快,你就会发现原本用于某个元素的样式开始冲突,并覆盖其他元素的样式。于是,你采用了一种命名空间机制来为这些样式创建一个作用域

或许可以像这样简单一些:

<form>
  <input placeholder="email" />
  <button>Submit<button>
</form>
/* form.css */
form button {
  background: blue;
}

或者更高级的,比如BEM

<form class="form">
  <input class="form__input" placeholder="email" />
  <button class="form__button">Submit<button>
</form>
/* form.css */
.form__button {
  background: blue;
}

我非常喜欢 BEM(以及其他基于约定的方法,例如 OOCSS、ITCSS 等)。只需在团队中采用一种通用约定,就能解决棘手的问题。

无论哪种方式,你在这里要解决的问题是将样式限定在特定上下文中。

同样的问题和解决方案也适用于 React。例如LoginForm

function LoginForm() {
  return (
    <form className="form">
      <input placeholder="username" type="text " />
      <input placeholder="password" type="password" />
      <button className="button">Submit</button>
    </form>
  )
}

我们无法预知这个按钮是否会与应用程序中其他地方的按钮冲突。因此,我们应该在这里使用某种作用域。即使使用 React,你仍然可以像使用 BEM 一样使用命名空间。

<button className="form__button">Submit</button>

故事从这里开始变得有趣了。在 React 组件中,我们不再编写普通的代码HTML,而是编写JSX……

上述 JSX 代码在构建时会转换为以下 JavaScript 代码块:

React.createElement(
  'button',
  { className: 'form__button' },
  'Submit'
)

现在,您可以完全掌控编程语言(JavaScript)的强大功能。您可以做到纯 CSS 无法实现的事情。

 

CSS-in-js 的前景

你可以将创建作用域或命名空间的任务交给语言本身,而不是手动完成。

CSS Modules是进入 css-in-js 生态系统的入门级工具。

你要写的是:

/* form.css */

button {
  /* look ma, no name spacing */
  background: blue;
}
import styles from './form.css'

function LoginForm() {
  return (
    <form>
      <button className={styles.button}>Submit</button>
    </form>
  )
}

styles.button编译后的结果如下:

function LoginForm() {
  return (
    <form>
      <button className="form__button__abc1">Submit</button>
    </form>
  )
}

这与手写样式非常相似,但它让你无需担心样式冲突。能够像局部作用域一样编写样式,让我感到无比自由。

 

下一代 CSS-in-JS 库

我们已经能够在工具/自动化方面利用语言的力量,我们能否也将其运用到写作风格方面呢?

争议就出在这里。每一种 CSS-in-JS 实现方式都略有不同,它们通过权衡取舍来实现某些新功能。

例如:jsxstyle允许您在元素上编写看起来像经典内联样式的样式,但通过 webpack 插件将其提取到一个文件中。

<Block component="button" backgroundColor="blue" />

另一方面,styled-components它允许你在 CSS 中混合运行时逻辑。这意味着你可以无需修改配置即可开始使用它,但你无法将样式提取出来。

const Button = styled.button`
  background: ${getBackground};
`

function getBackground(props) {
  if (props.appearance === 'primary') return 'blue'
  else if (props.appearance === 'disabled') return 'grey'
  else return 'white'
}

linaria它采用了一种有趣的折衷方案css,即通过标签在组件旁边创建类,并在构建过程中使用 Babel 插件提取这些类。这意味着你仍然可以使用 JavaScript,但它不能像之前的示例那样依赖于运行时逻辑。

const button = css`
  background: ${colors.blue};
`

function MyComponent() {
  return <button className={button}>Click me</button>
}

正如你所见,所有这些库都各有所长,但也都有其不足之处。正因如此,它们才如此具有争议性,你几乎可以找到任何一个库的缺陷。

css-modules它可以自动处理命名空间样式,但需要一些设置,这可能被视为过度设计(我们已经有了无需任何设置的手动 BEM)。

styled-components另一方面,它不需要任何 babel/webpack 设置,但需要在运行时存在该库,这会略微增加 javascript 包的大小。

 

你必须选择适合你项目的权衡方案。

我们之所以选择Auth0 的设计系统styled-components是因为它帮助我们基于一组底层令牌和设计模式创建灵活的组件。

上周,我需要构建很多包含表单逻辑的页面,我非常喜欢使用它,css-modules因为它帮助我编写全局样式和页面特定样式,而无需采用像 BEM 这样的手动方法。

你可以使用我朋友Michele Bertoli制作的这张对比表

希望这对你的旅程有所帮助。

西德


通讯

文章来源:https://dev.to/siddharthkp/why-do-we-keep-talking-about-css-in-js-35i8