[转]CSS 模块

CSS 模块 javascript

若是你想知道 CSS 最近发展的转折点,你应该选择去观看 Christopher Chedeau 在2014年11月的 NationJS 大会上作的名称为 CSS in JS 的分享。不得不说这是一个技术分水岭的时刻,一群不一样的思惟在本身的方向上就像粒子进入了高能漩涡中同样飞速发展。其中,在 React 或者是 React 相关的项目中编写 CSS 样式,React Style, jxstyleRadium 这三个算是最新的,最好的以及最具备可行性的方法。若是说“发明”是探索 最接近的可能 的一个实例(译者注:最接近的多是 Steven Johnson 于 2010 年提出来的一个概念),那么 Christopher 则是让许许多多的可能变得更加接近(译者注:上面三个工具中的两个灵感都是来自他的分享)。 css

This slide really hit home for a lot of folks

上图列出的这些都是在许多大型 CSS 代码库中存在的问题。Christopher 指出,只要将你的样式经过用 JS 去管理,这些问题都能很好的解决。不得不说这的确是有道理的,可是这种方法有它的复杂性并会带来其余的相关问题。其实只要看看浏览器是如何处理 :hover 伪类状态的,咱们就会发现有些东西在 CSS 中其实很早就解决了。 CSS 模块小组 以为咱们能够更加合理的解决问题:咱们能够继续保持 CSS 如今的样子,并在 styles-in-JS 社区的基础上创建更合理的改进。虽然咱们已经找到了解决办法同时又捍卫了 CSS 原始的美,但咱们仍然欠那些把咱们推向这个结果的那些人一声感谢。谢谢大家,朋友们! 下面让我来向你们解说一下什么是 CSS 模块而且为何它才是将来吧。 html

咱们是如此的强烈的思考着 CSS 的将来

第一步:像局部同样无需考虑全局冲突

在 CSS 模块中,每个文件被编译成独立的文件。这样咱们就只须要使用通用简单的类选择器名字就行了。咱们不须要担忧它会污染全局的变量。下面我就咱们编写一个拥有四个状态的按钮来讲明这个功能。 java

https://jsfiddle.net/pqnot81c/embedded/result/ react

CSS 模块以前咱们是怎么作的

咱们也许会使用 Suit/BEM 命名规范去进行样式命名,这样咱们的 HTML 和 CSS 代码看起来就像以下所示:webpack

/* 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 */

这样写看起来还挺棒的。使用 BEM 命令方式使咱们有了 4 个样式变量这样咱们没必要使用嵌套选择器。使用Button这种首字母大写的方法能够很好的避免与以前的代码或者是其余的依赖代码进行冲突。另外咱们使用了--语法这样能很清楚的显示出咱们的依赖 Class。 总的来讲,这样作可让咱们的代码更易于维护,可是它须要咱们在命名规范的学习上付出不少努力。不过这已是目前 CSS 能给出的最好的解决办法了。 git

CSS 模块出来以后咱们是怎么作的

CSS 模块意味着你今后不再必为你的名字太大众而担忧,只要使用你以为有最有意义的名字就行了:github

/* 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 模块被编译成为可能。web

/* components/submit-button.js */
import styles from "./submit-button.css";
buttonElem.outerHTML = `<button class=${styles.normal}>Submit</button>

你没必要担忧大众名字会致使命名冲突,编译后实际上类名是会自动生成并保证是惟一的。CSS 模块为你作好一切,最终编译成一个 CSS 与 JS 交互的ICSS 后缀文件(阅读这里了解更多)。所以,你的程序最终看起来可能会是这个样子的:bootstrap

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

若是你的类名变的和上面的例子差很少的话,那么恭喜你你成功了!

Success

命名约定

如今回过头来仔细看看咱们的示例代码:

/* 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_modules_plunkr.png这里是 CSS 模块的 Github 仓库地址,若是你有任何问题,请开 issue 提出。CSS 模块团队还很小,咱们尚未看到一些有用的例子,欢迎大家给咱们投稿。

最后的最后,祝你们开心写样式,幸福每一天!

原文:http://glenmaddern.com/articles/css-modules

本文连接:http://www.75team.com/post/1049.html

-- EOF --

发表于 2015-08-21 20:34:48 ,最后修改于 2016-03-15 16:31:48

« “文字”聚合、散出动画用Node.js开发微信墙简明教程 »

Comments

© 2016 -  奇舞团博客  - www.75team.com
Powered by ThinkJS & FireKylin

相关文章
相关标签/搜索