CSS 模块

原文:http://glenmaddern.com/articl…
译者:@公子

假如你想晓得 CSS 近来生长的转折点,你应当挑选去寓目 Christopher Chedeau 在2014年11月的 NationJS 大会上做的称号为 CSS in JS 的分享。不能不说这是一个手艺分水岭的时刻,一群差别的头脑在自身的方向上就像粒子进入了高能旋涡中一样飞速生长。个中,在 React 或许是 React 相干的项目中编写 CSS 款式,React Style, jxstyleRadium 这三个算是最新的,最好的以及最具有可行性的要领。假如说“发明”是探究 最靠近的可以 的一个实例(译者注:最靠近的多是 Steven Johnson 于 2010 年提出来的一个观点),那末 Christopher 则是让许许多多的可以变得越发靠近(译者注:上面三个东西中的两个灵感都是来自他的分享)。

《CSS 模块》

上图列出的这些都是在许多大型 CSS 代码库中存在的题目。Christopher 指出,只需将你的款式经由过程用 JS 去治理,这些题目都能很好的处置惩罚。不能不说这的确是有原理的,然则这类要领有它的复杂性并会带来其他的相干题目。实在只需看看浏览器是怎样处置惩罚 :hover 伪类状况的,我们就会发明有些东西在 CSS 中实在很早就处置惩罚了。

CSS 模块小组 以为我们可以越发合理的处置惩罚题目:我们可以继承坚持 CSS 如今的模样,并在 styles-in-JS 社区的基本上竖立更合理的革新。虽然我们已找到了处置惩罚办法同时又保卫了 CSS 原始的美,但我们依旧欠那些把我们推向这个结果的那些人一声谢谢。谢谢你们,朋友们!

下面让我来向人人申明注解一下什么是 CSS 模块而且为何它才是将来吧。

《CSS 模块》

第一步:像部份一样无需斟酌全局争执

在 CSS 模块中,每一个文件被编译成自力的文件。如许我们就只须要运用通用简朴的类挑选器名字就好了。我们不须要忧郁它会污染全局的变量。下面我就我们编写一个具有四个状况的按钮来申明这个功用。

https://jsfiddle.net/pqnot81c…

CSS 模块之前我们是怎样做的

我们或许会运用 Suit/BEM 定名范例去举行款式定名,如许我们的 HTML 和 CSS 代码看起来就像以下所示:

/* components/submit-button.css */
.Button { /* all styles for Normal */ }
.Button--disabled { /* overrides for Disabled */ }
.Button--error { /* overrides for Error */ }
.Button--in-progress { /* overrides for In Progress */
<button class="Button Button--in-progress">Processing...</button>

如许写看起来还挺棒的。运用 BEM 敕令体式格局使我们有了 4 个款式变量如许我们没必要运用嵌套挑选器。运用Button这类首字母大写的要领可以很好的防止与之前的代码或许是其他的依靠代码举行争执。别的我们运用了--语法如许能很清晰的显示出我们的依靠 Class。

总的来讲,如许做可以让我们的代码更易于保护,然则它须要我们在定名范例的学习上支付许多勤奋。不过这已是现在 CSS 能给出的最好的处置惩罚办法了。

CSS 模块出来以后我们是怎样做的

CSS 模块意味着你今后再也没必要为你的名字太群众而忧郁,只需运用你以为有最有意义的名字就好了:

/* components/submit-button.css */
.normal { /* all styles for Normal */ }
.disabled { /* all styles for Disabled */ }
.error { /* all styles for Error */ }
.inProgress { /* all styles for In Progress */

请注重我们这里并没有在任何处所运用 button 这个词。不过反过来想,为何我们肯定要运用它呢?这个文件已被定名成了 submit-button.css 了呀!既然在别的的语言中你不须要为你的部份变量增添前缀,没原理 CSS 须要加上这个糟糕的功用。

经由过程运用 require 或许 import 从 JS 中导入文件使得 CSS 模块被编译成为可以。

/* components/submit-button.js */
import styles from './submit-button.css';

buttonElem.outerHTML = `<button class=${styles.normal}>Submit</button>`

你没必要忧郁群众名字会颠倒定名争执,编译后实际上类名是会自动天生并保证是唯一的。CSS 模块为你做好统统,终究编译成一个 CSS 与 JS 交互的 ICSS 后缀文件(浏览这里相识更多)。因而,你的顺序终究看起来可以会是这个模样的:

<button class="components_submit_button__normal__abc5436">
  Processing...
</button>

假如你的类名变的和上面的例子差不多的话,那末祝贺你你胜利了!

《CSS 模块》

定名商定

如今回过头来细致看看我们的示例代码:

/* components/submit-button.css */
.normal { /* all styles for Normal */ }
.disabled { /* all styles for Disabled */ }
.error { /* all styles for Error */ }
.inProgress { /* all styles for In Progress */

请注重一切的类都是互相自力的,这里并不存在一个“ 基类 ”然后别的的类集成并“ 掩盖 ”它的属性这类状况。在 CSS 模块中 每一个类都必需包含这个元素须要的一切款式 (稍后会有细致申明)。这使得你在 JS 中运用款式的时刻有很大的差别:

/* Don't do this */
`class=${[styles.normal, styles['in-progress']].join(" ")}`

/* Using a single name makes a big difference */
`class=${styles['in-progress']}`

/* camelCase makes it even better */
`class=${styles.inProgress}`

固然,假如你的工资是根据字符串长度来盘算的话,你爱怎样做就怎样做吧!

React 示例

CSS 模块并非 React 特有的功用,然则不能不说在 React 中运用 CSS 模块会更爽。基于这个来由,我以为我有必要展现下面这个如飘柔般丝滑的示例:

/* components/submit-button.jsx */
import { Component } from 'react';
import styles from './submit-button.css';

export default class SubmitButton extends Component {
  render() {
    let className, text = "Submit"
    if (this.props.store.submissionInProgress) {
      className = styles.inProgress
      text = "Processing..."
    } else if (this.props.store.errorOccurred) {
      className = styles.error
    } else if (!this.props.form.valid) {
      className = styles.disabled
    } else {
      className = styles.normal
    }
    return <button className={className}>{text}</button>
  }
}

你完整不须要忧郁你的类定名会和全局的款式表定名争执,如许能让你的注重力更集合在组件上,而不是款式。我敢保证,运用过一次以后,你会再也不想回到本来的形式中去。

但是这仅仅是统统的最先。CSS 模块化是你的基本,但也是时刻来思索一下怎样把你的款式们都集合到一块了。

第二步:组件就是统统

上文中我提到了每一个类必需包含按钮差别状况下的一切的款式,与 BEM 定名体式格局上比拟,代码上可以区分以下:

/* BEM Style */
innerHTML = `<button class="Button Button--in-progress">`

/* CSS Modules */
innerHTML = `<button class="${styles.inProgress}">`

那末题目来了,你怎样在一切的状况款式中同享你的款式呢?这个答案就是 CSS 模块的强力兵器 – 组件

.common {
  /* all the common styles you want */
}
.normal {
  composes: common;
  /* anything that only applies to Normal */
}
.disabled {
  composes: common;
  /* anything that only applies to Disabled */
}
.error {
  composes: common;
  /* anything that only applies to Error */
}
.inProgress {
  composes: common;
  /* anything that only applies to In Progress */
}

composes这个关键词将会使.normal类将.common内的一切款式包含进来,这个有点像 Sass 的 @extends 语法。然则 Sass 依靠重写你的 CSS 文件到达结果,而 CSS 模块末了会经由过程 JS 编译导出,不须要修正文件(译者注:下面会有例子细致申明)。

Sass

根据 BEM 的定名范例,我用 Sass 的 @extends 写的话可以会像以下的代码:

.Button--common { /* font-sizes, padding, border-radius */ }
.Button--normal {
  @extends .Button--common;
  /* blue color, light blue background */
}
.Button--error {
  @extends .Button--common;
  /* red color, light red background */
}

编译后的 CSS 文件以下:

.Button--common, .Button--normal, .Button--error {
  /* font-sizes, padding, border-radius */
}
.Button--normal {
  /* blue color, light blue background */
}
.Button--error {
  /* red color, light red background */
}

你可以只须要个类来标记你的元素<button class="Button--error">,然则你能获得包含共有的和惯例的一切款式。这看起来真的异常凶猛,然则你须要注重这类语法在一些迥殊的状况下存在题目和圈套。Hugo Giraudel 在他的文章中做了很好的总结,谢谢他!

CSS 模块

composes 语法看起来很像 @extends 然则他们的事情体式格局是差别的。为了演示一下,让我们来看一个例子:

.common { /* font-sizes, padding, border-radius */ }
.normal { composes: common; /* blue color, light blue background */ }
.error { composes: common; /* red color, light red background */ }

编译后的文件多是像以下一样:

.components_submit_button__common__abc5436 { /* font-sizes, padding, border-radius */ }
.components_submit_button__normal__def6547 { /* blue color, light blue background */ }
.components_submit_button__error__1638bcd { /* red color, light red background */ }

JS 代码中经由过程 import styles from "./submit-button.css" 终究会返回:

styles: {
  common: "components_submit_button__common__abc5436",
  normal: "components_submit_button__common__abc5436 components_submit_button__normal__def6547",
  error: "components_submit_button__common__abc5436 components_submit_button__error__1638bcd"
}

所以我们依旧可以运用 style.normal 或许 style.error 在我们的代码中,依旧会有多个类款式渲染在我们的 DOM 上

<button class="components_submit_button__common__abc5436 
               components_submit_button__normal__def6547">
  Submit
</button>

这就是 composes 语法的凶猛的处所,你可以在不重写你的 CSS 的状况下对你的元素夹杂运用差别类的款式。

第三步:文件间同享

Sass 或许 LESS 中,你可以在每一个文件中运用 @import 在全局事情区间内同享款式。如许你就可以在一个文件中定义变量或许函数(mixins)并在你的别的组件文件中同享运用。如许做是有优点的,然则很快你的变量定名就会与别的的变量称号相争执(虽然它在别的一个悉数空间下),你不可防止的会重构你的 variables.scss 或许 settings.scss,末了你就会发明你已看不懂究竟哪一个组件依靠哪一个变量了。末了的末了你会发明你的配置文件变量称号冗余到变得异常不实用

针对上述题目依旧是有更好的处置惩罚办法的(事实上 Ben Smithett 的文章 Sass 和 Wepack 的夹杂运用 给了 CSS 模块话很大的启示,我引荐人人去读一读这篇文章),然则不管怎样做你照样范围在了 Sass 的全局环境下。

CSS 模块一次只运转一个文件,如许可以防止全局上下文的污染。而且像 JS 运用 import 或许 require 来加载依靠一样,CSS 模块运用 compose 来从另一个文件中加载:

/* colors.css */
.primary {
  color: #720;
}
.secondary {
  color: #777;
}
/* other helper classes... */
/* submit-button.css */
.common { /* font-sizes, padding, border-radius */ }
.normal {
  composes: common;
  composes: primary from "../shared/colors.css";
}

运用组件,我们可以深切到每一个像colors.css一样的基本款式表中,并随便重定名它。又由于组件只是改变了末了导出的类称号,而不是 CSS 文件自身,composes 语句在浏览器剖析之前就会被删除。

/* colors.css */
.shared_colors__primary__fca929 {
  color: #720;
}
.shared_colors__secondary__acf292 {
  color: #777;
}
/* submit-button.css */
.components_submit_button__common__abc5436 { /* font-sizes, padding, border-radius */ }
.components_submit_button__normal__def6547 {}
<button class="shared_colors__primary__fca929
               components_submit_button__common__abc5436 
               components_submit_button__normal__def6547">
  Submit
</button>

事实上,当它被浏览器剖析以后,我们的部份是不存在一个”normal“款式的。这是一件功德!这意味着我们可以增添一个部份名字有意义的对象(可以就叫”normal“)而不必在 CSS 文件中新增代码。我们运用的越多,对我们的网站会形成更少的视觉偏差以及在用户浏览器上更少的不一致。

题外话:空的类款式可以运用 csso 如许的东西来搜检去撤除。

第四步:功用单一模块

组件是异常壮大的,由于它确切的让你形貌了一个元素是什么,而不是它由那些款式构成。这是一种差别的体式格局去形貌观点示例(元素)到款式实体(款式划定规矩)之间的映照关联。让我们看一个简朴的 CSS 例子:

.some_element {
  font-size: 1.5rem;
  color: rgba(0,0,0,0);
  padding: 0.5rem;
  box-shadow: 0 0 4px -2px;
}

这些元素,款式都是迥殊简朴的。但是也存在着题目:色彩,字体大小,盒子暗影,内边距-这里的统统都是量身定制的,这让我们想要在别的处所复用这些款式的时刻变得有些难题。下面让我们用 Sass 重构这些这些代码:

$large-font-size: 1.5rem;
$dark-text: rgba(0,0,0,0);
$padding-normal: 0.5rem;
@mixin subtle-shadow {
  box-shadow: 0 0 4px -2px;
}

.some_element {
  @include subtle-shadow;
  font-size: $large-font-size;
  color: $dark-text;
  padding: $padding-normal;
}

这是一个进化版,然则我们仅仅只到达了一部份目的。事实上 $large-font-size$padding-normal 只是在名字上示意了它的用处,并不能在任何处所都实行。像 box-shadow 这类定义没办法赋值给变量,我们不能不运用 mixin 或许 @extends 语法来合营。

CSS 模块

经由过程运用组件,我们可以运用我们可复用的部份定义我们的组件:

.element {
  composes: large from "./typography.css";
  composes: dark-text from "./colors.css";
  composes: padding-all-medium from "./layout.css";
  composes: subtle-shadow from "./effect.css";
}

这类写法必将会有许多单一功用文件发生,但是经由过程运用文件体系来治理差别用处的款式比起用定名空间来讲要好的多。假如你想要从一个文件中导入多个类款式的话,有一种简朴的写法:

/* this short hand: */
.element {
  composes: padding-large margin-small from "./layout.css";
}

/* is equivalent to: */
.element {
  composes: padding-large from "./layout.css";
  composes: margin-small from "./layout.css";
}

这拓荒了一种可以,运用极细粒的类款式定义一些款式别号去给每一个网站运用:

.article {
  composes: flex vertical centered from "./layout.css";
}

.masthead {
  composes: serif bold 48pt centered from "./typography.css";
  composes: paragraph-margin-below from "./layout.css";
}

.body {
  composes: max720 paragraph-margin-below from "layout.css";
  composes: sans light paragraph-line-height from "./typography.css";
}

我对这类手艺异常感兴趣,我以为,它夹杂了像 Tachyons 相似的 CSS 原子手艺,像 Semantic UI 那样变量星散可读的优点(译者注:就是说 CSS 模块的定名简朴易懂,组件复用轻易)。

然则 CSS 模块化之路仅仅是刚刚最先,将来我们愿望人人能尝试更多为它写出谱写出新的篇章。

翻腾吧!CSS 模块!

我们愿望 CSS 模块化能有助于你和你的团队在你们现有的 CSS 和产物的基本上保护代码,让它变得更温馨更高效。我们已靠近可以的把分外的语法削减到起码,并只管确保语法和现有的变化不大。我们有 WebpackJSPMBrowserify 的示例代码,假如你们运用它们中的个中之一,你们可以参考一二。固然我们也在为 CSS 模块话的运用寻觅新的环境:服务端 NodeJS 的支撑正在举行,Rails 的支撑已提上议程预备举行了。

然则为了让它变得更简朴,我在 Plunker 上制作了一个预览示例,你不必装置任何东西就可以运转它:

《CSS 模块》

这里是 CSS 模块的 Github 堆栈地点,假如你有任何题目,请开 issue 提出。CSS 模块团队还很小,我们还没有看到一些有效的例子,迎接你们给我们投稿。

末了的末了,祝人人高兴写款式,幸运每一天!

    原文作者:公子
    原文地址: https://segmentfault.com/a/1190000003130731
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞