还不了解原子CSSJS?你想知道的都在哪里!

大家好,很高兴又见面了,我是"web 前端分享",由我带着大家一起关注前端前沿、深入前端底层技术,大家一起进步,也欢迎大家关注、点赞、收藏、转发!

随着 Facebook 和 Twitter 最近的生产部署,我认为一种新趋势正在慢慢发展:原子 CSS-in-JS。

在这篇文章中,我们将了解原子 CSS 是什么,它与功能性/实用性优先的 CSS(如 TailwindCSS)有何关系,以及那些大玩家正在其现代 React 代码库中采用它。

1.什么是原子 CSS?

1.1 什么是 BEM?

BEM 分别代表着:Block(块)、Element(元素)、Modifier(修饰符),是一种组件化的 CSS 命名方法和规范,由俄罗斯 Yandex 团队所提出。其目的是将用户界面划分成独立的(模)块,使开发更为简单和快速,利于团队协作开发。

BEM 的命名模式在社区中有着不同方式,以下为 Yandex 团队所提出的命名规则为:

.[Block 块]__[Element 元素]_[Modifier 修饰符]

当然任何一种规范,都是基于实际需求而定,便于团队开发和维护扩展,每个规范都是经过合理评估后所得出的一种“思路”和“建议”。比如 BEM 的下面例子(具体说明可阅读注释):



关于 BEM 的更多内容可继续阅读文末资料。

1.2 什么是 OOCSS?

OOP 已在 JavaScript 和后端语言中得到广泛使用,不止是 JS 才会有 OOP 面向对象模式,CSS 中的 OO 写法到现在已经不是一个新概念(2008 年由 Nicole Sullivan 提出,目标是通过应用 Java 和 Ruby 等变成语言普及的面向对象设计原则,使得动态 CSS 更易于管理)。

OOCSS,是 Object Oriented css 的缩写,旨在用最简单的方式编写 CSS 代码,从而提高代码重用性、可维护性、可扩展性。OOCSS 追求组件的复用,尽量不使用继承选择符,并且 class 命名比较抽象,一般不体现具体内容。以下面 HTML 为例:


  
    
  
  
    

这里是文本

下面是 CSS 内容:

media {
  padding: 10px;
}
.media:after {
  display: table;
  clear: both;
  content: ' ';
}
.media-image-container {
  float: left;
  margin-right: 10px;
}
.media-image {
  display: block;
}
.media-body {
  overflow: hidden;
}
/* 主题样式 */
.media-shadow {
  box-shadow: 1px 1px 3px rgba(0, 0, 0, 0.5);
}

1.2.1 分离结构和主题( Separate structure and skin)

分离结构和主题是在于将一些视觉样式效果(例如 background、color)作为单独的“主题”来应用。在上面的例子中的阴影效果,没有被直接写在 media 的样式规则内,而是被单独写在了一个名为 media-shadow 的 class 中。因此,它成为了可选择、可拆分的主题。如果不需要对应主题,什么也不要加,如果需要,加上对应的 class 即可。

1.2.2 容器与内容分离

分离容器和内容要求使页面元素不依赖于其所处位置。在上面的例子中,css 的选择符都很短,无继承选择符例如:

.header .media {
}

所以,这个图文排列的元件,可以在任何地方使用,且会有一致的外观。如果需要在特定的地方让这个元件看起来不一样,继续为这个元件增加 class,将“不一样的部分”作为可配置的选项,即元件的外观仍不依赖其所处位置。

1.3 什么是实用性优先(utility-first )?

上面介绍了各种 CSS 方法论,例如 BEM、OOCSS 等等。最近 Tailwind CSS 及其实用优先的概念流行了起来,其与 Functional CSS 和 Tachyon 很接近。比如下面的例子:


  
    ChitChat Logo
  
  
    ChitChat
    

You have a new message!

通过在 HTML 中使用各种 class ,而非为一个 class 添加复杂 css 属性来实现这种功能,比如下面的.chat-notificatio 类就很复杂:

.chat-notification {
  display: flex;
  max-width: 24rem;
  margin: 0 auto;
  padding: 1.5rem;
  border-radius: 0.5rem;
  background-color: #fff;
  box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
}

而本文关注的原子 CSS 就像实用优先 CSS 的极端版本,所有 CSS 类都有一个唯一的 CSS 规则。Atomic CSS 最早由 Thierry Koblentz (Yahoo!) 在 2013 年的 Challenging CSS Best Practices 中使用。比如下面的例子:

/* 原子CSS */
.bw-2x {
  border-width: 2px;
}
.bss {
  border-style: solid;
}
.sans {
  font-style: sans-serif;
}
.p-1x {
  padding: 10px;
}
/* 非原子CSS、因为这个class有2条规则 */
.p-1x-sans {
  padding: 10px;
  font-style: sans-serif;
}

使用原子 CSS 也许会带来结构层、表示层的耦合,比如当需要更改按钮颜色时,需要修改 HTML 而不是 CSS。 这种紧密耦合的关系在现代 CSS-in-JS 的 React 代码库中也变的可以接受,但似乎与现存的“关注点分离”的主流思是相违背的。

因为使用简单的类选择器,所以“关注点分离”也不再是什么大的问题,它会带来很多不一样的东西:

只是 Html 有点臃肿,这可能是服务器呈现的 Web 应用程序的一个问题,但是类名中的高冗余使用 gzip 可以很好地压缩,就像它适用于以前在 CSS 文件中找到的重复 css 规则一样。

您不需要为每种情况都使用原子 CSS,只需使用最常见的样式模式即可。一旦原子 CSS 准备就绪,它就不会改变或增长太多,而且可以很容易地缓存它(例如,可以将它附加到 vendor.css,并期望它不会在应用重新部署时失效)并用到其他地方。

2.原子 CSS 有没有限制?

原子 CSS 看起来很有趣,但它们也会有一些问题。人们通常手工编写原子 CSS,精心制定命名约定,但很难确保约定易于使用并保持一致,而且不会随着时间的推移而变得臃肿,特别在多人协作、大量使用的场景下。

在能够使用它的功能之前,您还需要预先处理一个原子样式表。如果 原子 CSS 是由其他人制作的,则您必须先学习类命名约定(即使您对 CSS 了如指掌),这是很多人不能接受的。同时,如果你要的样式原子 CSS 未提供,则您需要自行实现。

3.Tailwind 怎么样?

Tailwind 的做法非常方便,其确实也解决了其中的一些问题,但它也并没有真正为所有网站提供唯一的 CSS 文件。 相反,它仅提供共享范围和命名约定(shared scope and naming conventions), 从配置文件,它允许您生成自定义的 CSS。

// 默认情况下,Tailwind 将在项目的根目录中查找可选的 tailwind.config.js 文件,您可以在其中定义任何自定义项
module.exports = {
  content: ['./src/**/*.{html,js}'],
  theme: {
    colors: {
      blue: '#1fb6ff',
      purple: '#7e5bef',
      pink: '#ff49db',
      orange: '#ff7849',
      green: '#13ce66',
      yellow: '#ffc82c',
      'gray-dark': '#273444',
      gray: '#8492a6',
      'gray-light': '#d3dce6',
    },
    fontFamily: {
      sans: ['Graphik', 'sans-serif'],
      serif: ['Merriweather', 'serif'],
    },
    extend: {
      spacing: {
        '8xl': '96rem',
        '9xl': '128rem',
      },
      borderRadius: {
        '4xl': '2rem',
      },
    },
  },
};

与 postCSS 的插件相结合生成最终代码:

module.exports = {
  plugins: {
    tailwindcss: { config: './tailwindcss-config.js' },
  },
};

对于 Tailwind 的知识可以应用到其他应用程序,即它们不使用相同的类名,这让我想起了 React 的“Learn once, write anywhere”的理念。

相关报告显示 Tailwind 可以满足 90% 到 95% 的需求,看起来已经足够了,再也不需要经常编写一次性使用的 CSS 样式。此时您可能会问,为什么使用原子 CSS 而不是 Tailwind? 在执行 1 个规则,1 个类的原子 CSS 规则时,你会得到什么? 更大的 html 页面、不太方便的命名约定? 但是无论如何,Tailwind 已经有很多原子类可以直接使用了,这使得使用 Tailwind 很方便!

那么,我们是否应该放弃原子 CSS 的想法,而简单地使用 Tailwind?虽然 Tailwind 是一个很好的解决方案,但仍有一些问题没有解决:

与 Tailwind 相比,手写原子 CSS 可能不是最方便的。

4.与 CSS-in-JS 的比较

CSS-in-JS 和实用/原子 CSS 存在一定的关联。 这两种方法都提倡从标记中设置样式,以某种方式尝试模拟高性能的内联样式,这为它们提供了许多相似的属性。

Christopher Chedeau 极大地帮助在 React 生态系统中传播了 CSS-in-JS 的理念。 在多次演讲中,他解释了 CSS 的问题:

原子 CSS 也解决了其中一些问题,但绝对不是全部(特别是样式的非确定性解析)。 如果它们有相似之处,我们不能一起使用它们吗?

4.原子 CSS-in-JS

Atomic CSS-in-JS 可以看作是“自动原子 CSS”:

目前并非所有的 CSS-in-JS 库都支持原子 CSS , 其本身依赖于 CSS-in-JS 库的具体细节。下面是 2 个大规模原子 CSS-in-JS 部署示例:

你也可以使用下面的库:

5.React-Native-Web

React-Native-Web 是一个非常有趣的库:它允许在网络上呈现 React-Native 原语(React-Native primitives), 这里不过多谈论跨平台的移动/网络开发。

作为 Web 开发人员,您只需要了解 React-Native-Web 是一个常规的 CSS-in-JS 库,其由 Nicolas Gallagher 创建,它带有一小组原始 React 组件。 无论你在哪里看到 View,都可以在脑海中用 p 替换它。


6.Stylex

Stylex 是 Facebook 在 2020 年 重构时开发的新 CSS-in-JS 库。 他们计划有一天将其开源,可能会使用不同的名称,值得一提的是,React-Native-Web 的作者 Nicolas Gallagher 于 2 年前被 Facebook 聘用。 看到它的一些概念被 Facebook 重用也就不足为奇了。与 React-Native-Web 不同,Stylex 似乎并不专注于跨平台开发。

7.可扩展性(Scalability)

正如原子 CSS 所预期的那样,在 Twitter 和 Facebook 的应用上都看到了 CSS 的大量减少,而且遵循对数曲线。不过,简单的应用程序可能会有一定的时间、学习成本。


Facebook 分享了具体数字:

8.原子 CSS 的输入和编译输出

React-Native-Web、Stylex 这 2 个库具有相似且非常简单的 API,值得强调的是,React-Native-Web 将扩展 CSS shortand 语法,如 margin: 0。

9.生产环境验证(Production inspection)

一起看看 Twitter 站点的 HTML 结构:


再看看 Facebook 的页面结构:


多人看到这个可能会被吓到,但它确实有效,站点也没有任何问题。 在 Chrome 检查器中检查样式可能有点困难,但 devtools 可以提供帮助,解决这些问题。

10.CSS 规则顺序

与手写原子 CSS 不同,JS 库能够使样式不依赖于 CSS 规则插入顺序。 您可能知道,在规则冲突的情况下,获胜的不是类属性的最后一个类,而是样式表中最后插入的规则。 这些特异性问题可以仅通过使用简单的基于类的选择器来解决。

实际上,这些库避免在同一元素上输出具有冲突规则的类。它们确保 HTML 中声明的最后一个样式始终具有更高的优先级。 而“被重写的类”被过滤掉了,甚至没有进入 DOM。

const styles = pseudoLib.create({
  red: {color: "red"},
  blue: {color: "blue"},
});
// That p only will have a single atomic class (not 2!), for the blue color

  Always blue!

// That p only will have a single atomic class (not 2!), for the red color

  Always red!


如果一个类有多个规则,并且只有其中一个被覆盖,CSS-in-JS 库将无法在不删除未被覆盖的规则的情况下过滤该类。如果一个类有一个单一的速记规则,如 margin: 0,而重写是 marginTop: 10,这是同样的问题。 像 margin: 0 这样的简写语法被扩展到 4 个不同的类,并且该库能够更细粒度地过滤不应出现在 DOM 中的重写类。

11.您是否仍然喜欢 Tailwind?

了解所有 Tailwind 命名约定后,您就可以非常快速地编写 UI 代码, 像在 CSS-in-JS 中那样手动编写每个 CSS 的方式反而感觉效率较低。

如果你觉得它会带来效率提升的话,可以在原子 CSS-in-JS 框架之上构建自己的抽象,甚至可以在 JS 中重用 Tailwind 的命名约定。让我们来看一些 Tailwind 代码:


而如果采用 react-native-web-tailwindcss 的方案代码如下:

import { t } from 'react-native-tailwindcss';
;

或者使用其他颜色:

import { color } from 'react-native-tailwindcss';
;

就生产力而言,这并没有太大的不同,除了后者可以让你避免使用 TypeScript 的拼写错误。

12.结论

以上就是我要说的关于原子 CSS-in-JS 的所有内容,因为未在任何大型生产环境中部署、使用过原子 CSS、原子 CSS-in-JS 或 Tailwind。 如果文章有任何错误,可以直接在评论区留言。但是我认为原子 CSS-in-JS 是 React 生态系统中值得关注的趋势。

参考资料

https://www.bluesdream.com/blog/css-bem-interpretation.html

https://sebastienlorber.com/atomic-css-in-js

https://tailwindcss.com/docs/utility-first

https://news.68idc.cn/makewebs/css/20210923705881.html

https://github.com/TVke/react-native-tailwindcss

https://tailwindcss.com/docs/configuration

https://juejin.cn/post/6917073600474415117

展开阅读全文

页面更新:2024-02-20

标签:原子   可能会   样式   例子   规则   结构   代码   简单   方式   主题

1 2 3 4 5

上滑加载更多 ↓
推荐阅读:
友情链接:
更多:

本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828  

© CopyRight 2008-2024 All Rights Reserved. Powered By bs178.com 闽ICP备11008920号-3
闽公网安备35020302034844号

Top