ホームページ >ウェブフロントエンド >htmlチュートリアル >CSS module_html/css_WEB-ITnose
CSS の最近の開発のターニングポイントを知りたい場合は、2010 年に Christopher Chedeau によって提案されたコンセプトを見ることを選択する必要があります)、その後、Christopher は多くの可能性を近づけました (翻訳者注: 上記の 3 つと、この 2 つのインスピレーションは、ツールは彼の共有から生まれました)。
上の図は、多くの大規模な CSS コード ベースに存在する問題をリストしています。 Christopher 氏は、JS を使用してスタイルを管理している限り、これらの問題はうまく解決できると指摘しました。これは理にかなっていると言わざるを得ませんが、このアプローチには複雑さがあり、関連する他の問題も引き起こします。実際、ブラウザが :hover 疑似クラス状態をどのように処理するかを観察するだけでも、いくつかのことは CSS の**非常に早い段階*** で実際に解決されていることがわかります。
CSS モジュール チームは、より合理的な方法で問題を解決できると感じています。CSS を現状のまま維持し、styles-in-JS コミュニティを基盤にして、より合理的な改善を行うことができます。私たちは CSS 本来の美しさを保ちながら解決策を見つけましたが、この結果に私たちを後押ししてくれた方々には今でも感謝の気持ちを持っています。皆ありがとう!
CSS モジュールとは何か、そしてなぜ CSS モジュールが未来であるのかを説明しましょう。
CSS モジュールでは、各ファイルは独立したファイルにコンパイルされます。この方法では、一般的で単純なクラス セレクター名を使用するだけで済みます。グローバル変数を汚染することを心配する必要はありません。次に、この機能を説明するために 4 つの状態を持つボタンを作成します。
https://jsfiddle.net/pqnot81c/embedded/result/
スタイルの命名にはスーツの命名規則を使用して、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 の最初の文字を大文字にする方法を使用すると、前のコードや他の依存コードとの競合を効果的に回避できます。さらに、依存関係クラスを明確に示すために -- 構文を使用します。
一般に、これを行うとコードの保守が容易になりますが、命名規則を学ぶのに多大な労力を費やす必要があります。しかし、これはすでに 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 */
ご注意ください。ここでは単語ボタンを使用しないでください。しかし、逆に考えてみましょう。なぜそれを使用する必要があるのでしょうか?このファイルの名前は submit-button.css です。他の言語ではローカル変数にプレフィックスを付ける必要がないため、CSS にこの面倒な機能を追加する必要がある理由はありません。
require または import を使用して JS からファイルをインポートすることで、CSS モジュールをコンパイルすることができます。
そうですか実際、コンパイル後、クラス名は自動的に生成され、一意であることが保証されます。 CSS モジュールはすべてを行い、最終的には JS と対話する ICSS サフィックス ファイルにコンパイルします (詳細については、ここをお読みください)。したがって、プログラムは最終的に次のようになります:
/* components/submit-button.js */import styles from './submit-button.css';buttonElem.outerHTML = `<button class=${styles.normal}>Submit</button>`
クラス名が上記の例のように変更された場合、成功おめでとうございます!
さて、戻ってサンプルコードを詳しく見てみましょう:
<button class="components_submit_button__normal__abc5436"> Processing...</button>
すべてのクラスは互いに独立しており、「基本クラス」と他のクラスは存在しないことに注意してください。クラスは統合され、「この場合、そのプロパティをオーバーライドします。 CSS モジュールの各クラスには、この要素に必要なすべてのスタイルが含まれている必要があります (これについては後で詳しく説明します)。これは、JS でスタイルを使用する場合に大きな違いを生みます:
/* 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 モジュールは React 固有の機能ではありませんが、React で CSS モジュールを使用するとより楽しいと言わざるを得ません。このため、次のような楽しくスムーズな例を示す必要があると感じます:
/* 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}`
クラスの名前がグローバル スタイル シートの名前と競合することを心配する必要はありません。これにより、より注意を集中させることができます。スタイルではなくコンポーネントです。一度使ったら元のモードには戻れなくなること請け合いです。
しかし、これはすべての始まりにすぎません。 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 编译导出,不需要修改文件**(译者注:下面会有例子详细说明)。
按照 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 */}
你可以只需要**一**个类来标记你的元素36fbf595417e68841e981587687a7c59,但是你能得到包括共有的和特例的所有样式。这看起来真的非常厉害,但是你需要注意这种语法在一些特别的情况下存在问题和陷阱。Hugo Giraudel 在他的文章中做了很好的总结,感谢他!
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 语法来配合。
通过使用组件,我们可以使用我们可复用的部分定义我们的组件:
.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 模块的命名简单易懂,组件复用方便)。
但是 CSS 模块化之路仅仅是刚刚开始,未来我们希望大家能尝试更多为它写出谱写出新的篇章。
我们希望 CSS 模块化能有助于你和你的团队在你们现有的 CSS 和产品的基础上维护代码,让它变得更舒适更高效。我们已经接近可能的把额外的语法减少到最少,并尽量确保语法和现有的变化不大。我们有 Webpack,Rails 的支持已经提上议程准备进行了。
但是为了让它变得更简单,我在 Plunker 上制作了一个预览示例,你不用安装任何东西就可以运行它:
这里还很小,我们还没有看到一些有用的例子,欢迎你们给我们投稿。