ホームページ > 記事 > ウェブフロントエンド > グローバル CSS_html/css_WEB-ITnose の終了
2016-01-05
»読書体験の向上
元のリンク
すべての CSS セレクターは同じグローバル スコープを共有します。
CSS を長期間使用している人は誰でも、CSS のグローバルな特性に悩まされているはずです。ドキュメントベースの Web ページの時代に設計されたこのモデルは、今日の Web アプリケーションに直面すると、あまりにも弱すぎます。優れた開発環境を提供します。
すべてのセレクターは、不要な要素を指定したり、他のセレクターと競合したりするなど、予期しない影響を与える可能性があります。さらに予想外なことは、セレクターの重みがグローバル スコープで無効になる可能性さえあり、その結果、スタイルがページ内で何の役割も果たさなくなることです。
CSS ファイルを変更するときは常に、これらのスタイルが地球環境に与える影響を慎重に考慮する必要があります。コードの最小限の保守性を確保するためだけに、これほど多くの仕様を必要とするフロントエンド テクノロジはありません。
でも、こんなはずじゃない。
グローバルなスタイルを忘れる時が来ました。
ネイティブ CSS を採用する時期が来ました。
JavaScript コミュニティー、Browserify、Webpack、および JSPM によって提供されるツールのおかげで、これらのモジュールは依存関係を明示的に指定し、少数の API インターフェイスをエクスポートするだけで済みます。
しかし、何らかの理由で CSS は依然として無視されているようです。
私自身を含む私たちの多くは、これまで長い間 CSS を使用してきたため、CSS のローカル スコープの欠如は、誰もが Shadow DOM をサポートするブラウザを使用するまで解決できない問題であると感じています。 。
グローバル スコープの問題を解決するために、OOCSS、SMACSS、BEM、SUIT などの一連の命名規則を使用しています。これらの仕様は、名前の競合を回避する方法と、使用可能なスコープ ルールのセットを提供します。
これが優れた CSS への重要なステップであることは疑いの余地がありませんが、それでも、これらの仕様のどれもスタイル シートの本当の問題を解決するものではありません。どの仕様を使用することを選択しても、依然としてグローバル セレクターに固執しています。
しかし、2015 年 4 月 22 日、すべてが変わりました。
少し前のブログ投稿「次世代 JavaScript コンポーネントの BEM」で述べたように、Webpack を使用して JavaScript モジュールに CSS をインポートできます。これに慣れていない場合は、まずこの記事を読んでください。そうしないと、以降の重要なポイントを見逃してしまう可能性があります。
Webpack の css-loader を使用して、次のようにコンポーネントの CSS をインポートします:
require('./MyComponent.css');
一見すると、JavaScript ではなく CSS をインポートしていることに気づかなくても、このコード行はかなり奇妙に見えます。
通常、require 呼び出しは現在のスコープに何かを提供します。そうでない場合は、グローバルに影響を与えるモジュールの導入が原因である可能性が高く、通常、これは設計が悪いです。
しかし、これは CSS です - グローバル効果は必須です。
少なくとも私たちはそう考えています。
2015 年 4 月 22 日、Webpack のたゆまぬ作者である Tobias Koppers は、css-loader ローダーの新機能の最初のイテレーションを提出しました。当時、この機能はプレースホルダーと呼ばれていましたが、現在はローカル スコープとして知られています。
この機能を使用すると、CSS ファイルから JavaScript コードにクラス名をインポートできます。
簡単に言うと、次のようには書かないでください:
require('./MyComponent.css');
次のように書きます:
import styles form './MyComponent.css';
それでは、この例では、styles 変数は何に等しいでしょうか?
CSS ファイルが何を出力するかを理解するために、スタイル シートがどのように記述されているかを見てみましょう:
:local(.foo) { color: red;}:local(.bar) { color: blue;}
上記のコードでは、css-loader ローダーの特定の構文を使用しています: local(.identifier ) 2 つの ID (foo と bar) を出力します。
JavaScript ファイルでは、これらの識別子に対応するクラス名を使用できます。たとえば、React を使用します。
import styles from './MyComponent.css';import React, { Component } from 'react';export default class MyComponent extends Component { render() { return ( <div> <div className={styles.foo}>Foo</div> <div className={styles.bar}>Bar</div> </div> ); }}
重要なことは、これらの識別子がグローバル環境内で一意のクラス名を生成するということです。
スコープを達成するためにすべてのセレクターに長いプレフィックスを追加する必要はなくなりました。従来のグローバル セレクター パターンとは異なり、コンポーネントは名前の競合を引き起こすことなく、独自の foo および bar 識別子を定義できます。
さらに重要なことは、ここでどのような大きな変化が起こったのかを知る必要があるということです。
CSS ファイルを変更しても、Web ページの他の要素には影響しないと確信しています。 CSS ファイルに優れたスコープ パターンを導入しました。
グローバル CSS の利点 - 実用的なクラスをさまざまなコンポーネントで再利用できる... - はこのモードでも維持されます。主な違いは、他の手法と同様に、必要なクラスを明示的にインポートできることです。コードでは、地球環境についていかなる仮定も行うことはできません。
最近では、命名規則に注意深く従うのではなく、開発中にスタイルをカプセル化することによって、保守可能な CSS を作成する必要があります。
由于这种作用域化的方式,类名交由Webpack进行控制生成。幸好,这是可以进行配置的。
默认情况下,css-loader加载器将标识转换为哈希值。
例如:
:local(.foo) { … }
会被编译成:
._1rJwx92-gmbvaLiDdzgXiJ { … }
在开发时,这对debug来说没有任何帮助。为了让类名更可读,我们可以在Webpack的配置文件中给css-load加载器传个参数来配置类名的格式:
loaders: [ ... { test: /\.css$/, loader: 'css?localIdentName=[name]__[local]___[hash:base64:5]' }]
这样,foo类名将会编译为:
.MyComponent__foo___1rJwx { … }
现在我们可以清晰的看到标识的名称,同样也能辨别出它所在的组件。
使用 node_env环境变量,我们可以为开发环境和打包环境配置不同的类名生产方式。
loader: 'css?localIdentName=' + ( process.env.NODE_ENV === 'development' ? '[name]__[local]___[hash:base64:5]' : '[hash:base64:5]')
当我们知道了这个功能,我们迫不及待的给项目的样式都进行本地化。如果我们已经通过BEM命名规范来本地化CSS样式,那么我们只需要简单的修改。
有趣的是,这模式的引入是如此的简单。我们大部分的CSS文件只需要加入local标识:
:local(.backdrop) { … }:local(.root_isCollapsed .backdrop) { … }:local(.field) { … }:local(.field):focus { … }
等等…
在开发中,需要用到全局选择器的地方并不多。所以这让我们自然而然地想到了一个非常重要的问题。
假如选择器默认是本地作用域 - 并不需要特殊的语法 - 而全局的选择器则是可选择性的加入,这样会不会比较好呢?
译注:作者的意思是,当我们引入了CSS作用域的技术后,我们会发现大部分模块的CSS都会进行本地化,只有少量的全局CSS存在,那么,我们应该对这少部分的全局CSS进行特殊处理,而不是对大部分的具有作用域的CSS进行标识。
假设我们可以这么写:
.backdrop { … }.root_isCollapsed .backdrop { … }.field { … }.field:focus { … }
即使一般情况下看来,这些选择器的表达模糊不清,但通过css-loader加载器的本地化可以解决这个问题,而且也能够把它们作用域限定在当前模块中。
在少数的情况下,我们不可避免的要使用到全局样式,我们可以将它们用指定的 :global语法来标记出来。
比如我们用 ReactCSSTransitionGroup来生成全局的类名:
.panel :global .transition-active-enter { … }
在这例子中,我们不只将panel标识限定在模块中, 而且我们还定义了一个全局的类名 transition-active-enter。
当我们着手研究该如何实现这种“默认自带作用域”的类名语法时,我们发现其实这并不是非常困难。
为了实现这功能,我们要用到 PostCss- 这是一个出色的工具,他能让你将自定义的CSS转换器编写成组件。如今最流行的CSS构建工具之一 - Autoprefixer- 就是用PostCss开发出来的独立插件工具。
为了实现本地CSS,我已开源了一个实验性的PostCss插件,叫做 postcss-local-scope。该工具依旧在开发中,你可以在个人的项目中使用。
如果你正在使用Webpack,那么你可以在CSS构建流程中结合使用 postcss-loader加载器和 postcss-local-scope工具。这里不贴出例子,我已经创建了一个示例代码库,里面给出了示例 - postcss-local-scope-example。
让人激动的是,引入本地作用域只是个开始。
通过构建工具生成类名,我们能实现更多的事情。长远来看,我们可以不再需要人工参与编译而是让计算机来优化编辑结果。
不久将来,我们将可以让构建工具自动地生成组件共用的类,在编辑的过程中对可复用样式进行优化。
当你已经试着开始使用本地CSS时,你真的再也不会使用以前的那套老做法了。在样式表中体验一下真正的本地CSS吧 - 使用这种兼容所有浏览器的方法 - 这真的让你会终生难忘的。
引入本地CSS对我们看待CSS造成一连串的显著影响,无论是命名习惯,复用原则,还是样式提取分离,但这仅仅是CSS本地化这新领域的起点。
一旦你将它赋予实践,你会知道,全局CSS完结这一天终将会到来。CSS的未来属于本地化。
注意:组件之间可复用样式的自动优化将是一个惊人的飞跃,但这需要比我更聪明的人来实现。希望这会是你。☺
##附录
2015 年 5 月 24 日: post-local-scope ツールの元のアイデアは Tobias koppers によって Webpack に導入されました。これは、このプロジェクトの使用が推奨されなくなったことを意味します。モジュール識別子を css-loader ローダーで使用して、CSS モジュール化を実装できるようになりました。 CSS モジュールの使用法を示すために、css-loader ローダーを使用して CSS モジュールの例を作成しました。これには、コンポーネント間でスタイルをインテリジェントに再利用するためのクラス継承も含まれています。