• home > webfront > style > css3 >

    CSS模块化方案PK:BEM/OOCSS/SMACSS/ITCSS、CSS Modules、CSS-in-JS

    Author:zhoulujun Date:

    JavaScript 社区中的 AMD、CMD、CommonJS、ES Modules 等类似,CSS 社区也诞生了相应的模块化解决方案:BEM、OOCSS、SMACSS、ITCSS、CSS Modules 、 CSS-in-JS

     JavaScript 社区中的 AMD、CMD、CommonJS、ES Modules 等类似,CSS 社区也诞生了相应的模块化解决方案:BEM、OOCSS、SMACSS、ITCSS,以及 CSS Modules 和 CSS-in-JS 等。

    根据这些 CSS 模块化方案的特点,我简单的将它们分为了三大类:

    1. CSS 命名方法论:通过人工的方式来约定命名规则。

    2. CSS Modules:一个 CSS 文件就是一个独立的模块。

    3. CSS-in-JS:在 JS 中写 CSS。

     CSS 模块化方案

    CSS 命名方法论

    为了避免 CSS 选择器命名冲突的问题,以及更好的实现 CSS 模块化,CSS 社区在早期诞生了一些 CSS 命名方法论,如 BEM、OOCSS、SMACSS、ITCSS、SUITCSS、Atomic CSS 等。

    它们几乎都有一个共同的特点——为选择器增加冗长的前缀或后缀,并试图通过人工的方式来生成全局唯一的命名。这无疑会增加了类命名的复杂度和维护成本,也让 HTML 标签显得臃肿。

    BEM

    BEM(Block Element Modifier)是一种典型的 CSS 命名方法论,由 Yandex 团队(鹅版百度)在 2009 年前提出,它的核心思想是 通过组件名的唯一性来保证选择器的唯一性,从而保证样式不会污染到组件外

    BEM 命名规约是 .block-name__element-name--modifier-name,即 .模块名__元素名--修饰器名 三个部分,用双下划线 __ 来明确区分模块名和元素名,用双横线  -- 来明确区分元素名和修饰器名。你也可以在保留 BEM 核心思想的前提下,自定义命名风格,如驼峰法、使用单下划线、使用单横线等。

    在 BEM 中不建议使用子代选择器,因为每一个类名已经都是全局唯一的了,除非是 block 相互嵌套的场景。

    优点:

    • 命名规范:BEM 强调清晰的命名约定,使得样式层次结构和关系一目了然,便于团队协作和维护。

    • 可重用性:由于 BEM 强调组件化设计,样式容易被复用,降低了重复代码的风险。

    • CSS 体系结构:BEM 通过命名规则和结构,强制开发者遵循一套标准的样式体系结构,减少了样式冲突的可能性。

    缺点:

    • 冗长的类名:BEM 的命名方式导致类名较长,可能影响代码的可读性,尤其是在复杂的组件结构中。

    • 学习成本:需要团队中的所有成员都熟悉并遵循 BEM 的命名规范,否则容易产生不一致的问题。

    • 灵活性不足:由于 BEM 的强规范性,在处理一些灵活或动态的样式需求时可能显得不够灵活。

    OOCSS

    OOCSS(Object-Oriented CSS)面向对象的 CSS,它借鉴了 OOP(面向对象编程)的抽象思维,主张将元素的样式抽象成多个独立的小型样式类,来提高样式的灵活性和可重用性

    OOCSS由 Nicole Sullivan 在 2008 年提出!CSS Lint最初是由Nicholas C. Zakas和Nicole Sullivan编写的

    Nicole Sullivan 可以说是Web技术布道者,前端性能优化和CSS专家 http://www.stubbornella.org

    OOCSS 有两个基本原则:

    1. 独立的结构和样式:即不要将定位、尺寸等布局样式与字体、颜色等表现样式写在一个选择器中。

    2. 独立的容器和内容:即让对象的行为可预测,避免对位置的依赖,子元素即使离开了容器也应该能正确显示。

    OOCSS 要求为这个容器创建更多的“原子类”,并且每个样式对应一个类,这样是为了后面可以重复使用这些组件的样式,避免重复写相同的样式

    在 OOCSS 中,类名既要能传递对象的用途,也要有通用性,比如:

    .mt-5 { margin-top: 5px; }
    .mt-10 { margin-right: 10px }
    .mt-15 { margin-bottom: 10px; }

    其实可以改为

    .mt-min { margin-top: 5px; }
    .mr-small { margin-right: 10px }
    .mb-medium { margin-bottom: 10px; }

    类似的,还有颜色类。不要一blue  red yellow 来直接命名,需要语义化,如:

    --primary-color: #3a84ff;
    --success-color: #2dcb56;
    --warning-color: #ff9c01;
    --danger-color: #ea3636;
    --default-color: #63656e;

    OOCSS适合做库的公用组件库封装,在大项目中使用,非常蛋疼

    Tailwind CSS 不完全属于 OOCSS 方案

    OOCSS(Object-Oriented CSS)的特点

    OOCSS 强调将样式封装在独立的对象中,这些对象具有可复用性和可维护性。它鼓励开发者将样式分解成更小的、可组合的类,从而提高 CSS 的可扩展性。

    Tailwind CSS 的特点

    Tailwind CSS 是一种原子化 CSS 框架,它提供了一组高度可定制的、低级别的 CSS 类。这些类可以组合在一起,快速构建出复杂的 UI。

    OOCSS缺点

    1、样式与结构的耦合

    • 问题:OOCSS 强调将结构(如布局)和外观(如颜色、字体)分离,但在实践中,这种分离可能会导致样式与 HTML 结构之间的耦合变得更加紧密。例如,为了实现某些布局效果,可能需要在 HTML 中增加额外的 div 或类名,这些标记仅仅是为了实现特定的样式需求。

    • 影响:这种耦合会导致 HTML 代码臃肿,降低了代码的语义性和可读性。

    2. 类名污染

    • 问题:OOCSS 的一个核心思想是通过复用类名来实现不同组件间的样式共享。这可能导致一个页面上出现大量相同的类名,尤其是在复杂的页面或组件中。

    • 影响:大量的重复类名可能导致代码难以调试,尤其是在样式冲突时很难追踪问题的根源。此外,还可能导致 CSS 选择器的优先级管理变得复杂。

    3. 代码膨胀

    • 问题:OOCSS 强调可复用性,这意味着开发者在定义样式时,可能会倾向于创建大量的小的、可复用的类。虽然这有助于减少重复代码,但同时也可能导致样式表中出现大量的小类,增加 CSS 文件的体积。

    • 影响:这不仅会导致样式表的加载时间变长,还可能会对样式表的可维护性产生负面影响,尤其是在大型项目中。

    4. 学习曲线

    • 问题:OOCSS 的理念与传统的 CSS 开发方式有所不同,开发者需要理解如何将结构与样式分离,以及如何创建可复用的对象和组件。

    • 影响:对于没有使用过 OOCSS 的团队或个人来说,理解并有效地应用 OOCSS 需要一定的时间和学习成本。这可能会延长项目的初期开发时间。

    5. 灵活性不足

    • 问题:OOCSS 强调复用和模块化,但在某些情况下,这种复用可能会限制开发者的灵活性。特别是在需要创建独特的、非标准化的设计时,OOCSS 的规范可能会显得束缚。

    • 影响:开发者可能不得不为了遵循 OOCSS 的原则而进行额外的工作,或在某些情况下,放弃 OOCSS 而采用更为灵活的解决方案。

    6. 跨项目的适应性差

    • 问题:OOCSS 在某些特定项目中可能效果很好,但当你试图将 OOCSS 的对象和组件移植到另一个项目时,可能会发现它们并不适用,尤其是在设计风格和布局大不相同时。

    • 影响:跨项目复用时,可能需要对已有的 OOCSS 代码进行大量调整,削弱了其“可复用性”的优势。


    SMACSS

    SMACSS(Scalable and Modular Architecture for CSS)即可伸缩及模块化的 CSS 结构,由 Jonathan Snook 在 2011 年雅虎时提出。

    SAMCSS 按照部件的功能特性,将其划分为五大类:

    1. 基础(Base)是为HTML元素定义默认样式,可以包含属性、伪类等选择器。

    2. 布局(Layout)会将页面分为几部分,可作为高级容器包含一个或多个模块,例如左右分栏、栅格系统等。

    3. 模块(Module)又名对象或块,是可重用的模块化部分,例如导航栏、产品列表等。

    4. 状态(State)描述的是任一模块或布局在特定状态下的外观,例如隐藏、激活等。

    5. 主题(Theme)也就是换肤,描述了页面的外观,它可修改前面四个类别的样式,例如链接颜色、布局方式等。

    SMACSS 推荐使用前缀来区分不同部件:

    1. 基础规则是直接作用于元素的,因此不需要前缀。

    2. 布局的前缀是 l- 或 layout-,例如 .l-table、.layout-grid 等。

    3. 模块的前缀是 m- 或模块自身的命名,例如 .m-nav、.card、.field 等。

    4. 状态的前缀是 is-,例如 .is-active、.is-current 等。

    5. 主题的前缀是 theme-,例如 .theme-light、.theme-dark 等。

    现在大部分的项目都是采用SMACSS方案,但是BEM有的缺点,他都有!


    ITCSS

    ITCSS(Inverted Triangle CSS,倒三角 CSS)是一套方便扩展和管理的 CSS 体系架构,它兼容 BEM、OOCSS、SMACSS 等 CSS 命名方法论ITCSS 使用 分层 的思想来管理你的样式文件,类似服务端开发中的 MVC 分层设计

    ITCSS(Inverted Triangle CSS)是在2016年由阿里的Harry Roberts提出的——甲骨文、微软、亚马逊,2014年9月,以首席架构师的身份加入阿里巴巴速卖通(aliexpress),并于2017年3月晋升为CTO。

    安利一下:郭东白的架构课

    ITCSS 将 CSS 的样式规则划分成以下的几个层次:

    1. Settings:项目使用的全局变量,比如颜色,字体大小等等。

    2. Tools:项目使用的 mixins 和 functions。到 Tools 为止,不会生成具体的 CSS 代码。

    3. Generic:最基本的设定,比如 reset.css、normalize.css 等。

    4. Base:最基础的元素(elements),比如 img、p、link、list 等。

    5. Objects:某种设计模式,比如水平居中,

    6. Components:UI 组件,比如 button、switch、slider 等。

    7. Trumps:用于辅助和微调的样式,只有这一层才可以使用 !important

    ITCSS 的分层逻辑越往下就越具体,越局限在某个具体的场景。

    5e4cb70583db4d3bb8b5ed8bb2d604b1~tplv-k3u1fbpfcp-zoom-in-crop-mark_1512_0_0_0.webp

    根据 ITCSS 的思想,你可以这样组织你的 CSS 样式文件:

    stylesheets/
    ├── settings/
    │   ├── colors.scss
    │   ├── z-layers.scss
    │   └── breakpoints.scss
    ├── tools/
    │   ├── mixins.scss
    │   └── functions.scss
    ├── generic/
    │   ├── box-sizing.scss
    │   └── normalize.scss
    ├── base/
    │   ├── img.scss
    │   └── list.scss
    ├── objects/
    │   ├── grid.scss
    │   └── media.scss
    ├── components/
    │   ├── buttons.scss
    │   └── slider.scss
    ├── trumps/
    │   ├── widths.scss
    │   └── gaps.scss
    └── index.scss

    下面是几个基于 ITCSS 的模版项目,可供参考:

    ITCSS缺点

    • 配置复杂: ITCSS 的配置可能比较复杂,尤其是当项目规模较大或涉及多个团队协作时。这可能会增加维护成本,并引入潜在的错误。

    • 命名复杂:为了遵循 ITCSS 的层次结构和命名规则,开发者可能需要创建复杂的类名或变量名,尤其是在处理多个相似的样式或组件时。复杂的命名规则可能会导致代码的可读性下降,特别是在跨团队协作或有新成员加入时,复杂的命名方式会让人感到困惑。

    • 层次之间的依赖性使灵活性受限、复杂性增加: ITCSS 的严格层级结构在某些情况下可能会限制 CSS 的灵活性。如果需要进行一些非常特殊的样式调整,可能需要打破既定规则,从而增加代码的复杂性。

    • 与第三方库的集成问题:在使用 CSS Modules 时,如何与第三方 UI 库或组件库集成可能会带来一些挑战。这些库通常会使用全局样式或特定的类名,而 CSS Modules 则会局部化样式,这可能导致样式冲突或无法正确应用第三方样式。

    只要是靠人去保证代码质量总是不靠谱的,人的状态有起伏,但是机器没有,因此推荐用机器去解决这些问题。所以命名法从工程角度去看,主要依赖于人的主观能动性(总有不靠谱的时候)!

    CSS Modules是解决上述问题的一个傻瓜式方案

    CSS Modules

    手写命名前缀后缀的方式让开发者苦不堪言,于是 CSS Modules 这种真正的模块化工具就诞生了。

     上面提到的这些 CSS 命名方法论,虽然已经不适用于当今的自动化工作流和大前端环境,但是他们有其诞生的时代背景,也确实推动了 CSS 模块化的发展,其背后的设计思想同样值得我们学习,甚至有时候我们仍然能在某些场合下看到他们的影子。

    CSS Modules 允许我们像 import 一个 JS Module 一样去 import 一个 CSS Module。每一个 CSS 文件都是一个独立的模块,每一个类名都是该模块所导出对象的一个属性。通过这种方式,便可在使用时明确指定所引用的 CSS 样式。并且,CSS Modules 在打包时会自动将 id 和 class 混淆成全局唯一的 hash 值,从而避免发生命名冲突问题。这样, 全局作用域污染、样式冲突、样式复用困难、 样式的可维护性、命名复杂性都解决了

    CSS Modules 特性:

    • 作用域:模块中的名称默认都属于本地作用域,定义在 :local 中的名称也属于本地作用域,定义在 :global 中的名称属于全局作用域,全局名称不会被编译成哈希字符串。

    • 命名:对于本地类名称,CSS Modules 建议使用 camelCase 方式来命名,这样会使 JS 文件更干净,即 styles.className。 但是你仍然可以固执己见地使用 styles['class-name'],允许但不提倡。

    • 组合:使用 composes 属性来继承另一个选择器的样式,这与 Sass 的 @extend 规则类似。

    • 变量:使用 @value 来定义变量,不过需要安装 PostCSS 和 postcss-modules-values 插件。

    这里仅罗列一些 CSS Modules 的核心特性,更具体的用法可以参考 官网 或 阮老师的《CSS Modules 用法教程》

    配合 CSS 预处理器使用

    使用 CSS Modules 时,推荐配合 CSS 预处理器(Sass/Less/Stylus)一起使用。

    CSS 预处理器提供了许多有用的功能,如嵌套、变量、mixins、functions 等,同时也让定义本地名称或全局名称变得容易。

    CSS-in-JS

    React 的出现,打破了以前“关注点分离”的网页开发原则,因其采用组件结构,而组件又强制要求将 HTML、CSS 和 JS 代码写在一起。表面上看是技术的倒退,实际上并不是。

    React 是在  JS 中实现了对 HTML 和 CSS 的封装,赋予了 HTML 和 CSS 全新的“编程能力”。对于 HTML,衍生了 JSX 这种 JS 的语法扩展,你可以将其理解为 HTML-in-JS;对于 CSS,衍生出一系列的第三方库,用来加强在 JS 中操作 CSS 的能力,它们被称为 CSS-in-JS。

    CSS-in-JS最初由Facebook的员工react核心成员Vjeux在2014年的NationJS会议上提出。

    随着 React 的流行以及组件化开发模式的深入人心,这种"关注点混合"的新写法逐渐成为主流。

    Any application that can be written in JavaScript, will eventually be written in JavaScript. —— Jeff Atwood

    CSS-in-JS 库目前已有几十种实现,你可以在 CSS in JS Playground 上快速尝试不同的实现。下面列举一些流行的 CSS-in-JS 库:

    styled-components

    styled-components 是目前最流行的 CSS-in-JS 库,在 React 中被广泛使用。

    具体使用参看文档:https://styled-components.com/docs

    CSS-in-JS缺点

    CSS-in-JS 也有一些潜在的缺点,如增加了运行时开销、可能影响首次渲染性能、学习曲线较陡等。相比之下,CSS Modules 更接近传统 CSS 开发方式,性能开销较小,但功能相对简单。

    总体来说:

    • 对于需要高度动态样式和复杂主题管理的项目,CSS-in-JS 可能更有优势;

    • 而对于追求更轻量级解决方案的项目,CSS Modules 可能是更好的选择。

    选择CSS in JS之前,推荐读一下:

    https://github.com/ascoders/weekly/blob/master/前沿技术/263.精读《我们为何弃用%20css-in-js》.md

    https://codeburst.io/css-in-js-is-like-replacing-a-broken-screwdriver-with-your-favorite-hammer-c9765c9ee43b

    https://hackernoon.com/stop-using-css-in-javascript-for-web-development-fa32fb873dcc


    vue scope

    vue scope 感觉像是 CSS Modules+CSS-in-JS 的异形!

    在背后,Vue 会为该组件内所有的元素都加上一个全局唯一的属性选择器,形如 [data-v-5298c6bf],这样在组件内的 CSS 就只会作用于当前组件中的元素。

    scope 简单的属性选择器性能是高于包含选择器。但是往往项目中有大量的嵌套属性选择器。

    使用BEM 、ITCSS可以用@root 来规避嵌套。性能是优于scope方案的。还有就是scope样式穿透问题非烦人!

    当然,个人还是推荐 CSS Modules






    参考文章:

    CSS 模块化方案探讨(BEM、OOCSS、CSS Modules、CSS-in-JS ...) https://juejin.cn/post/6947335144894103583

    css 模块化相关 BEM/CSS Modules https://github.com/cisen/blog/issues/795

    CSS Module用法总结 https://www.cnblogs.com/Ewarm/p/14150561.html

    一些 CSS 管理方案的优缺点 https://ukn.me/yi-xie-css-guan-li-fang-an-de-you-que-dian.html




    转载本站文章《CSS模块化方案PK:BEM、OOCSS、CSS Modules、CSS-in-JS》,
    请注明出处:https://www.zhoulujun.cn/html/webfront/style/css3/2024_0807_9211.html




    转载本站文章《CSS模块化方案PK:BEM/OOCSS/SMACSS/ITCSS、CSS Modules、CSS-in-JS》,
    请注明出处:https://www.zhoulujun.cn/html/webfront/style/css3/2024_0808_9212.html