Home > Article > Web Front-end > CSS Modules explained in detail
Cascading Style Sheet
We know that the full name of css is called cascading style sheet, this " What exactly does “cascading” mean?
One explanation is that if you first write a style rule (player 1):
.title { color: silver; }
and then write a similar one after ( Player 2):
.title { color: gold; }
Because their names are the same, Player 2 will fight with Player 1 (I want you to pretend to be me!). The result is that player 2 wins, the element with class name is title, and the final color value is gold.
It's like this in css. A war may break out at any time if there is a disagreement, and the losing party will be overwritten by the winning party. The word "cascading" can be said to describe this process vividly.
So, why is there such a layering (zhàn zhēng)?
css scope issue
In javascript, you can achieve such a combination:
var title = "silver"; (function(){ var title = "gold"; console.log(title); // gold }()); console.log(title); // silver
Using the function scope of javascript, two players with the same name can get along well with each other.
But back to the style rules in css, the situation is completely different.
CSS is not a programming language, but if we want to add a scope concept to it, it is: only global scope.
No matter how many CSS files are split into, no matter how they are introduced, all style rules are in the same scope. As long as the selectors are similar, there is a possibility of overwriting.
Strategies to reduce mutual influence
In order to reduce mutual influence and avoid unexpected style overrides, we have always thought of many ways.
For example, if you take over an old project left by someone else, and you want to add a new title element, you will consciously not use a vague class name like .title because it is too easy to duplicate the name. . In the end, the name you use may be:
.module-sp-title { color: deepskyblue; }
Even if you decide to use the name .title, you will also qualify it with the include selector:
.module-1 .title { font-size: 18px; } /* ... */ .module-2 .title { font-size: 14px; }
The names of .module-1 and .module-2 should be unique. Such code is very common in componentized (modular) development style.
In addition, some well-known CSS theories, such as SMACSS, will recommend that you use the l- or layout- prefix for all layout styles to distinguish them.
There are many similar approaches, but in the final analysis, they are all trying to provide a reasonable naming convention. And reasonable naming convention is indeed an effective strategy for organizing CSS code.
Now, we have new strategies available, CSS Modules is one of them.
Modularization of technology flow
CSS Modules is a technology flow strategy for organizing css code, which will provide a default local scope for css .
How do CSS Modules do it? Let’s look at a simple example of CSS Modules.
There is such an html element:
<h2 id="example_title" class="title">a title for CSS Modules</h2>
According to the ordinary css writing method, we can add styles to it like this:
.title { background-color: snow; }
Now we switch to CSS Modules. First, the css remains unchanged. Then, modify the writing method of html. Instead of writing html directly like this, add it dynamically in the javascript file instead (the css file is called main.css):
var styles = require("./main.css"); var el = document.getElementById("example_title"); el.outerHTML = '<h2 class="' + styles.title + '">a title for CSS Modules</h2>';
Hey, it’s required A css file? Right, so webpack is used. After compilation, html and css will become like this:
#You will probably understand when you see such an unsightly class name. CSS Modules cannot change the global scope of css. By nature, it relies on dynamically generating class names to achieve local scope. Obviously, such a class name can be unique. No matter how casually the original css code is written, it can be converted in this way to obtain non-conflicting css code.
The simulated local scope doesn't matter either, it's reliable.
This is the end of the CSS Modules example, but you must have as many questions as I did when I first saw it.
Application details of CSS Modules
How to enable CSS Modules
"webpack compiles css for me I've used it before, why doesn't it look like this when I use it?"
Generally speaking, the way to require a css file is:
require("./main.css");
But in In the previous example, the writing method var styles = require("./main.css"); was used. This is like saying, I want the styles in this css file to be local, and then I can use them as needed.
There are many ways to apply CSS Modules in projects. Currently, the more commonly used one is to use webpack’s css-loader. You can enable CSS Modules by writing css-loader?modules in the webpack configuration file. For example, the one used in the previous example:
module: { loaders: [{ test: /\.css$/, loader: 'style!css?modules' }] }
I discovered that the css-loader I had been using was originally Does it have this function? In fact, CSS Modules is indeed a new feature that was incorporated into css-loader later.
Custom-generated class name
“
名字都这样了,还怎么调试?”
为css-loader增加localIdentName参数,是可以指定生成的名字。localIdentName的默认值是[hash:base64],一般开发环境建议用类似这样的配置:
{ test: /\.css$/, loader: 'style!css?modules&localIdentName=[name]__[local]___[hash:base64:5]' }
同样应用到前面的例子里,这时候就会变成这样的结果:
这样是不是要有意义多了?
如果是线上环境,可以考虑用更短的名字进一步减小css文件大小。
CSS Modules下的html
(看了前面例子里的el.outerHTML = ...后)
“什么,outerHTML?class名还要拼接?你家html才这么写呢!”
很遗憾,CSS Modules官方的例子,也是这个意思:要使用CSS Modules,必须想办法把变量风格的class名注入到html中。也就是说,html模板系统是必需的,也正是如此,相比普通css的情况,CSS Modules的html写起来要更为费劲。
如果你搜一下CSS Modules的demo,可以发现大部分都是基于React的。显然,虚拟DOM风格的React,搭配CSS Modules会很容易(ES6):
import styles from './ScopedSelectors.css'; import React, { Component } from 'react'; export default class ScopedSelectors extends Component { render() { return ( <p className={ styles.root }> <p className={ styles.text }>Scoped Selectors</p> </p> ); } };
如果不使用React,还是那句话,只要有办法把变量风格的class名注入到html中,就可以用CSS Modules。原始的字符串拼接的写法显然很糟糕,但我们可以借助各种模板引擎和编译工具做一些改进。下面请看一个用Jade的参考示例。
想象一下你有一个用普通css的页面,但你想在一小块区域使用CSS Modules。这一块区域在一个容器元素里:
<p id="module_sp_container"></p>
后用jade来写html(关联的css文件为module_sp.css):
- styles = require("./module_sp.css"); h2(class=styles.title) a title for CSS Modules
接下来,仍然是在javascript里添加这段jade生成的html:
var el = document.getElementById("module_sp_container"); var template = require("./main.jade"); el.innerHTML = template();
最后,记得在css-loader启用CSS Modules的同时,增加jade-loader:
{ test: /\.jade$/, loader: 'jade' }
编译运行,就可以得到想要的结果。除Jade以外,还有些其他CSS Modules的html应用方案,推荐参考github上的这篇issue。
目前CSS Modules还在发展中,而且也在考虑改进CSS Modules下的html写作体验。CSS Modules团队成员有提到一个叫CSS Modules Injector的未来规划项目,目的是让开发者不用javascript也可以使用CSS Modules(这就很接近原生html + css的组合了)。
CSS Modules下的样式复用
“样式都是唯一的了,怎么复用?”
我们已经说了挺多普通css单个全局作用域的坏处。但对应的,这也有一个很大的好处,就是便于实现样式的复用。css理论OOCSS也是在追求这一点。
CSS Modules提供一个composes方法用于样式复用。例如,你有一个btn.css里有一条:
.btn{ display: inline-block; }
然后,你在另一个CSS Module的module_sp.css里可以这样引入它:
.btn-sp{ composes: btn from "./btn.css"; font-size: 16px; }
那么,这个p.btn-sp的DOM元素将会是:
可以看到,composes的用法比较类似sass的@extend,但不同于@extend的是,composes并不增加css里的选择符总量,而是采用组合多个class名的形式。在这个例子里,原本仅有1个class的p.btn-sp,变成了2个class。
因此,CSS Modules建议只使用1个class就定义好对应元素所需的全部样式。它们会再由CSS Modules转换为适当的class组合。
CSS Modules团队成员认为composes是CSS Modules里最强大的功能:
For me, the most powerful idea in CSS Modules is composition, where you can deconstruct your visual inventory into atomic classes, and assemble them at a module level, without duplicating markup or hindering performance.
更详细的composes的用法及其理解,推荐阅读CSS Modules: Welcome to the Future。
其他可能有用的补充
和已有的普通css共存
很多项目会引入Bootstrap、Materialize等框架,它们是普通的、全局的css。此外,你也可能自己会写一些普通css。如何共存呢?CSS Modules团队成员对此提到过:
a CSS Module should only import information relative to it
意思是,建议把CSS Modules看做一种新的css,和原来的普通css区分开来。比如,composes的时候,不要从那些普通的css里去取。
在css-loader里通过指定test、include、exclude来区分它们。保持CSS Modules的纯净,只有想要应用CSS Modules的css文件,才启用CSS Modules。
只转换class和id
经过我自己的测试,CSS Modules只转换class和id,此外的标签选择符、伪类等都不会被转换。
建议只使用class。
一个CSS Module的输出
简单用console.log()就可以查看CSS Module的输出:
var styles = require("./main.css"); console.log("styles = ", styles);
结果类似这样:
{ "btn-sp": "_2SCQ7Kuv31NIIiVU-Q2ubA _2r6eZFEKnJgc7GLy11yRmV", title: "_1m-KkPQynpIso3ofWhMVuK" }
这可以帮助理解CSS Modules是怎样工作的。
预编译器
sass等预编译器也可以用CSS Modules,对应的loader可能是这样:
{ test: /\.scss$/, loader: 'style!css?modules!resolve-url!sass?sourceMap' }
注意不要因为是sass就习惯性地用嵌套写法,CSS Modules并不适合使用包含选择符。
建议的命名方式
CSS Modules会把.title转换为styles.title,由于后者是用在javascript中,因此驼峰命名会更适合。
如果像我之前那样写.btn-sp,需要注意在javascript中写为styles["btn-sp"]。
此外,你还可以为css-loader增加camelCase参数来实现自动转换:
{ test: /\.css$/, loader: 'style!css?modules&camelCase', }
这样即便你写.btn-sp,你也可以直接在javascript里用styles.btnSp。
结语
无论是一直以来我们认真遵循的命名约定,还是这个新的CSS Modules,目的都是一样的:可维护的css代码。我觉得就CSS Modules基本还是在写css这一点来说,它还是很友好的。
虽然本文为了严谨,结果写了相当长的篇幅,但希望你读过之后,还能觉得CSS Modules是简单易懂的。因为这样,我就达成我的目的:扣题,了。
推荐教程:《PHP》
The above is the detailed content of CSS Modules explained in detail. For more information, please follow other related articles on the PHP Chinese website!