关于css:CSS-模块化方案探讨BEMOOCSSCSS-ModulesCSSinJS

5次阅读

共计 9760 个字符,预计需要花费 25 分钟才能阅读完成。

全文共 4000 余字,预计破费 30 分钟。

家喻户晓,CSS 依据选择器名称去全局匹配元素,它没有作用域可言,比方你在页面的两个不同的中央应用了一个雷同的类名,先定义的款式就会被笼罩掉。CSS 始终不足模块化的概念,命名抵触的问题会继续困扰着你。每次定义选择器名称时,总会顾及其余文件中是否也应用了雷同的命名,这种影响在组件开发中尤为显著。💣💣💣

现实的状态下,咱们开发一个组件的过程中,应该能够随便的为其中元素进行命名,只须要保障其语义性即可,而不用放心它是否与组件之外的款式发生冲突。

与 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 命名方法论,如 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 互相嵌套的场景。

<!-- 示例模块 -->
<div class="card">
  <div class="card__head">
    <ul class="card__menu">
      <li class="card__menu-item">menu item 1</li>
      <li class="card__menu-item">menu item 2</li>
      <li class="card__menu-item card__menu-item--active">menu item 3</li>
      <li class="card__menu-item card__menu-item--disable">menu item 4</li>
    </ul>
  </div>
  <div class="card__body"></div>
  <div class="card__foot"></div>
</div>
.card {}
.card__head {}
.card__menu {}
.card__menu-item {}
.card__menu-item--active {}
.card__menu-item--disable {}
.card__body {}
.card__foot {}

应用 Sass/Less/Stylus 的父元素选择器 & 能够更高效的编写 BEM:

.card {&__head {}
  &__menu {
    &-item {&--active {}
      &--disable {}}
  }
  &__body {}
  &__foot {}}

OOCSS

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

OOCSS 有两个根本准则:

  1. 独立的构造和款式。即不要将定位、尺寸等布局款式与字体、色彩等体现款式写在一个选择器中。
  2. 独立的容器和内容。即让对象的行为可预测,防止对地位的依赖,子元素即便来到了容器也应该能正确显示。

比方:咱们有一个容器是页面的 1/4 宽,有一个蓝色的背景,1px 灰色的边框,10px 的左右边距,5px 的上边距,10px 的下边距。以前对于这样一个款式,咱们经常给这个容器创立一个类,并把这些款式写在一起。像上面这样。

<div class="box"></div>

<style>
  .box {
    width: 25%;
    margin: 5px 10px 10px;
    background: blue;
    border: 1px solid #ccc;
  }
</style>

然而应用 OOCSS 的话,咱们不能这样做,OOCSS 要求为这个容器创立更多的“原子类”,并且每个款式对应一个类,这样是为了前面能够重复使用这些组件的款式,防止反复写雷同的款式,就拿这个实例来说,咱们给这个容器减少上面的类:

<div class="size1of4 bgBlue solidGray mt-5 ml-10 mr-10 mb-10"></div>

<style>
  .size1of4 {width: 25%;}
  .bgBlue {background: blue;}
  .solidGray {border: 1px solid #ccc;}
  .mt-5 {margin-top: 5px;}
  .mr-10 {margin-right: 10px}
  .mb-10 {margin-bottom: 10px;}
  .ml-10 {margin-left: 10px;}
</style>

OOCSS 最大的长处是让款式可复用性最大化,也可能显著缩小整体的 CSS 代码数量。毛病也很显著,你须要为每个元素收集一大堆类名,这可是一个不小的体力活 😅。

在 OOCSS 中,类名既要能传递对象的用处,也要有通用性,例如 mod、complex、pop 等。如果将 CSS 类命名的太语义化,例如 navigation-bar,那么就会将其限度在导航栏,无奈利用到网页的其它地位。

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 等。
<form class="layout-grid">
  <div class="field">
    <input type="search" id="searchbox" />
    <span class="msg is-error">There is an error!</span>
  </div>
</form>

ITCSS

ITCSS(Inverted Triangle CSS,倒三角 CSS)是一套不便扩大和治理的 CSS 体系架构,它兼容 BEM、OOCSS、SMACSS 等 CSS 命名方法论。ITCSS 应用 分层 的思维来治理你的款式文件,相似服务端开发中的 MVC 分层设计。

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 的分层逻辑越往下就越具体,越局限在某个具体的场景。

依据 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 的模版我的项目,可供参考:

  • https://github.com/itcss/itcs…
  • https://github.com/gpmd/itcss…
  • https://github.com/cameronroe…

CSS Modules

📚 下面提到的这些 CSS 命名方法论,尽管曾经不适用于当今的自动化工作流和大前端环境,然而他们有其诞生的时代背景,也的确推动了 CSS 模块化的倒退,其背地的设计思维同样值得咱们学习,甚至有时候咱们依然能在某些场合下看到他们的影子。

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

CSS Modules 容许咱们像 import 一个 JS Module 一样去 import 一个 CSS Module。每一个 CSS 文件都是一个独立的模块,每一个类名都是该模块所导出对象的一个属性。通过这种形式,便可在应用时明确指定所援用的 CSS 款式。并且,CSS Modules 在打包时会主动将 id 和 class 混同成全局惟一的 hash 值,从而防止产生命名抵触问题。

这里仅列举一些 CSS Modules 的外围个性,更具体的用法能够参考 官网 或 阮老师的《CSS Modules 用法教程》。

CSS Modules 个性:

  • 作用域:模块中的名称默认都属于本地作用域,定义在 :local 中的名称也属于本地作用域,定义在 :global 中的名称属于全局作用域,全局名称不会被编译成哈希字符串。
  • 命名 :对于本地类名称,CSS Modules 倡议应用 camelCase 形式来命名,这样会使 JS 文件更洁净,即 styles.className
    然而你依然能够回心转意地应用 styles['class-name'],容许但不提倡。🤪
  • 组合:应用 composes 属性来继承另一个选择器的款式,这与 Sass 的 @extend 规定相似。
  • 变量:应用 @value 来定义变量,不过须要装置 PostCSS 和 postcss-modules-values 插件。
/* style.css */
:global(.card) {padding: 20px;}
.article {background-color: #fff;}
.title {font-size: 18px;}
// App.js
import React from 'react'
import styles from './style.css'

export default function App() {
  return (<article className={styles.article}>
      <h2 className={styles.title}>Hello World</h2>
      <div className="card">Lorem ipsum dolor sit amet.</div>
    </article>
  )
}

编译后果:

<style>
  .card {padding: 20px;}
  .style__article--ht21N {background-color: #fff;}
  .style__title--3JCJR {font-size: 18px;}
</style>

<article class="style__article--ht21N">
  <h2 class="style__title--3JCJR">Hello World</h2>
  <div class="card">Lorem ipsum dolor sit amet.</div>
</article>

CSS Modules 集成

在 webpack 中应用 CSS Modules(开启 css-loader 的 modules 个性):

// webpack.config.js -> module.rules
{test: /\.(c|sa|sc)ss$/i,
  exclude: /node_modules/,
  use: [
    'style-loader',
    {
      loader: 'css-loader',
      options: {
        importLoaders: 2,
        // 开启 CSS Modules
        modules: true,
        // 借助 CSS Modules,能够很不便地主动生成 BEM 格调的命名
        localIdentName: '[path][name]__[local]--[hash:base64:5]',
      },
    },
    'postcss-loader',
    'sass-loader',
  ],
},

在 PostCSS 中应用 CSS Modules(应用 postcss-modules 插件):

// postcss.config.js
module.exports = {
  plugins: {
    'postcss-modules': {generateScopedName: '[path][name]__[local]--[hash:base64:5]',
    },
  },
}

配合 CSS 预处理器应用

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

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

:global(.title) {color: yellow;}

:global {
  .global-class-name {color: green;}
}

VSCode 扩大反对

在 VSCode 中写 CSS Modules 代码,默认是没有主动提醒和跳转至定义处的性能,不够智能。

能够装置 CSS Modules 扩大。

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。

随着 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:https://github.com/styled-com… 33k(举荐
  • emotion:https://github.com/emotion-js… 13k
  • Radium:https://github.com/Formidable… 7k(已不再保护)
  • Styled System:https://github.com/styled-sys… 7k
  • styled-jsx:https://github.com/vercel/sty… 6k
  • JSS:https://github.com/cssinjs/jss 6k

styled-components 💅

styled-components 是目前最风行的 CSS-in-JS 库,在 React 中被宽泛应用。

它应用 ES6 提供的模版字符串性能来结构“款式组件”。

// styles.js
import styled, {css} from 'styled-components'

// 创立一个名为 Wrapper 的款式组件 (一个 section 标签, 并带有一些款式)
export const Wrapper = styled.section`
  padding: 10px;
  background: deepskyblue;
`

// 创立一个名为 Title 的款式组件 (一个 h1 标签, 并带有一些款式)
export const Title = styled.h1`
  font-size: 20px;
  text-align: center;
`

// 创立一个名为 Button 的款式组件 (一个 button 标签, 并带有一些款式, 还接管一个 primary 参数)
export const Button = styled.button`
  padding: 10px 20px;
  color: #333;
  background: transparent;
  border-radius: 4px;

  ${(props) => props.primary && css`
    color: #fff;
    background: blue;
  `}
`
// App.js
import React from 'react'
import {Wrapper, Title, Button} from './styles'

// 而后,像应用其余 React 组件一样应用这些款式组件
export default function App() {
  return (
    <Wrapper>
      <Title>Hello World, this is my first styled component!</Title>
      <Button>Normal Button</Button>
      <Button primary>Primary Button</Button>
    </Wrapper>
  )
}

更多应用技巧(更具体的内容请参考 官网文档):

  • 能够通过插值的形式给款式组件传递参数(props),这在须要动静生成款式规定时特地有用。
  • 能够通过构造函数 styled() 来继承另一个组件的款式。
  • 应用 createGlobalStyle 来创立全局 CSS 规定。
  • styled-components 会为主动增加浏览器兼容性前缀。
  • styled-components 基于 stylis(一个轻量级的 CSS 预处理器),你能够在款式组件中间接应用嵌套语法,就像在 Sass/Less/Stylus 中的那样。
  • 强烈推荐应用 styled-components 的 Babel 插件 babel-plugin-styled-components(当然这不是必须的)。它提供了更好的调试体验的反对,比方更清晰的类名、SSR 反对、压缩代码等等。
  • 你也能够在 Vue 中应用 styled-components,vue-styled-components,不过如同没人会这么做 \~
  • 默认状况下,模版字符串中的 CSS 代码在 VSCode 中是没有智能提醒和语法高亮成果的,须要装置 扩大。

在 Vue 中编写 CSS 的正确姿态

形式一:应用 Scoped CSS(举荐)

<style> 区块增加 scoped 属性即可开启“组件款式作用域(Scoped CSS)”。

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

<template>
  <header class="header">header</header>
</template>

<style scoped>
.header {background-color: green;}
</style>

编译后果:

<header class="header" data-v-5298c6bf>header</header>

<style>
.header[data-v-5298c6bf] {background-color: green;}
</style>

形式二:应用 CSS Modules

<style> 区块增加 module 属性即可开启 CSS Modules。

在背地,Vue 会为组件注入一个名为 $style 的计算属性,并混同类名,而后你就能够在模板中通过一个动静类绑定来应用它了。

<template>
  <header :class="$style.header">header</header>
</template>

<style module>
.header {background-color: green;}
</style>

编译后果:

<header class="App__header--382G7">header</header>

<style>
.App__header--382G7 {background-color: green;}
</style>

在 React 中编写 CSS 的正确姿态

React 并没有给咱们提供与 Vue 的 scoped 相似的个性,咱们须要通过其余形式来实现 CSS 模块化。

  1. 应用 styled-components:styled-components 是最风行也是最好用的 CSS-in-JS 库,它将 CSS、JS 以及 React 开发中最风行的一些语法整合起来,易上手,且功能强大。
  2. 应用 CSS Modules:在内部治理 CSS,而后将类名映射到组件外部,他会为每个 class 都调配一个全局惟一 hash。另外,这两个插件会帮你更好地在 React 中应用 CSS Modules:react-css-modules、babel-plugin-react-css-modules。

CSS Modules 与 styled-components 是两种截然不同的 CSS 模块化计划,它们最实质的区别是:前者是在内部治理 CSS,后者是在组件中治理 CSS。两者没有孰好孰坏,如果你能承受 CSS-in-JS 这种编程模式,更举荐应用 styled-components。如果一时无奈承受,感觉其过于激进了,那就用 CSS Modules。It doesn’t matter,抉择了哪一个,就用哪一个的体系去治理我的项目就好了。

参考资料

  1. BEM: A New Front-End Methodology — Smashing Magazine
  2. Battling BEM CSS: 10 Common Problems And How To Avoid Them — Smashing Magazine
  3. An Introduction To Object Oriented CSS (OOCSS) — Smashing Magazine
  4. MicheleBertoli/css-in-js: React: CSS in JS techniques comparison
  5. CSS Modules 用法教程 – 阮一峰的网络日志
  6. CSS in JS 简介 – 阮一峰的网络日志
正文完
 0