Or you can say, CSS modules for us to solve what pain point. For the past I write Web style experience, specifically, can be summed up as the following points:
The process is this: you now have two modules, a, B, you might write your own styles for each of these two modules, such as A.CSS, B.css, and look at the code
Here's the style:
/* a.css */.text { color: red;}/* b.css */.text { color: blue;}
Import into the Portal app
// App.jsimport A from './A.js'import B from './B.js'element.innerTHML = 'xxx'
Because the styles are uniformly loaded into the portal, the actual styles are combined (here tentatively mix.css) in the order of display:
/* mix.css *//* a.css */.text { color: red;}/* b.css */.text { color: blue;}
According to the CSS layout rules, so the following style will overwrite the previous style declaration, the final effect is the text
color blue
of the rule, this is the global style overlay, the same, which also exists in the js
same, so the introduction of modularity, In JS, you can isolate different modules with an immediate execution function expression.
var moduleA = (function(document, undefined){ // your module code})(document)var moduleB = (function(document, undefined){ // your module code})(document)
In the CSS in order to introduce modularity, then can only be namespace
achieved through, and this will bring new problems, this will be described below
Selectors with nested levels too deep In order to solve the problem of global style conflict, we have to introduce some special naming namespace
to distinguish scope
, but often some name is not namespace
clear enough, it will cause to think the next style will not overwrite, it is necessary to add a new namespace
to distinguish, Eventually an element might end up with a display style similar to the following:
.widget .table .row .cell .content .header .title { padding: 10px 20px; font-weight: bold; font-size: 2rem;}
Using 7 selectors on the display of the previous element, the following questions are summed up:
- Depending on the parsing rules of the CSS selector, it is possible to know that the deeper the hierarchy, the more times it will be compared. Of course, in more cases, the level of nesting may be deeper, in addition, the use of the class selector, and the use of class selectors, the whole page may be more impact on rendering.
- Increased unnecessary byte overhead
- Semantic confusion, when there are too many in the document,
content
title
and item
These generic class names, you may have to spend long time to know exactly which element they are using
- Poor scalability, more constraints, less extensibility
"Note" CSS rendering rules can refer to this article to explore the principle of CSS parsing
will result in code redundancy Since CSS does not use a modular function like JS, you may have written a common style class in a CSS file, and you need a style like this in another CSS, and you may write one more time, similar to this
/* a.css */.modal { position: absolute; top: 0; bottom: 0; left: 0; right: 0; z-index: 1; background-color: rgba(0, 0, 0, 0.7);}.text { color: red;}/* b.css */.modal { position: absolute; top: 0; bottom: 0; left: 0; right: 0; z-index: 1; background-color: rgba(0, 0, 0, 0.7);}.text { color: blue;}
Then when merging into APP.CSS, it will be written two times, although the style will not be affected, but this is actually a kind of waste of bytes, of course, the above situation is entirely possible through the common global style to achieve the purpose, but this code duplication is usually not informed of the situation occurs.
Some solutions For some of the above issues, there are some solutions, as follows:
CSS Preprocessor (sass/less, etc.) Sass,less usage here no longer repeat, if not clear, you can consult the relevant information to understand.
The biggest advantage of CSS preprocessor is that it can support the introduction of modules, the use of JS to write CSS, to solve some scope
of the confusion and code redundancy problems, but can not be completely avoided. At the same time, there is no solution to the problem of global style conflicts
One of the SASS
files is this:
/* app.sass */@import './reset'@import './color'@import './font'
Can actually be compiled after all, is still a file, so inevitably there will be conflict style
BEM (Block Element Modifier)
There is only of problems in computer Science:cache invalidation and naming Things-phil Karlton
BEM
is to solve the naming conflict and better semantics of the birth.
BEM noun explanation
Block: The logic and page functions are independent of the page components, is a reusable unit, features are as follows:
- Can be arbitrarily nested combinations
- Can be placed anywhere on any page without compromising functionality and appearance
- Reusable, interface can have any number of instances of the same block
- Element:block component, dependent on block existence (cannot be used if block is out)
[optional] Define the appearance and behavior of blocks and element, just like HTML attributes, to make the same block look different
Naming rules Block
As the smallest reusable unit, arbitrary nesting does not affect functionality and appearance, naming can be header
, and menu
so on
<style> .header { color: #042; }</style><div class="header">...</div>
Element
Dependency block exists, there is no separate meaning, naming semantics as close as possible to block, such as title
, and so on item
<style> .header { color: #042; } .header__title { color: #042; }</style><div class="header">
Modifier
Is the status display of an element, such as,, active
current
selected
<style> .header--color-black { color: #000; } .header__title--color-red { color: #f00; }</style><div class="header header--color-black">
Description
- Block completely independent, can be nested, a header is a block,header under the search box can also be a block
- There is no way
Block__Element-father__Element-son_Modifer
to bem this kind of name, only three levels.
- Modifier can be added to block and element
- Modifier loads blocks and element as an additional class name, just to change the state and to preserve the original class
A complete example <form class="form form--theme-xmas form--simple"> <input class="form__input" type="text" /> <input class="form__submit form__submit--disabled" type="submit" /></form>
.form { }.form--theme-xmas { }.form--simple { }.form__input { }.form__submit { }.form__submit--disabled { }
Reference Links:
- Get BEM
- BEM (Block-element-modifier)
- How do you look at the naming of BEM in CSS?
BEM solves the problem of module reuse, global naming conflict, and can be extended to a certain extent when used with pre-processing CSS, but it still has its problems:
- It is very difficult to name the elements that need to be semantically represented in a hierarchy that is deeply nested.
- For multi-person collaboration, a uniform naming convention is required, which also creates additional effort
CSS Modules Having said so much, I finally got to the text.
What is CSS Modules According to the CSS modules repo, this is the case:
CSS files in which all class names and animation names is scoped locally by default.
So CSS modules is not a formal statement or a browser implementation, but a build tool (Webpack or browserify) to enable all classes to reach the scope of a process.
What the CSS Modules solves
- Global naming conflicts, because CSS modules only cares about the component itself, so long as the component itself is not named conflict, there is no such problem, the class name of a component after it is compiled may be:
/* App.css */.text { color: red;}/* 编译之后可能是这样的 */.App__text___3lRY_ { color: red;}
Naming is unique, thus guaranteeing that the global does not conflict.
You can use composes
to introduce styles in your own module and the style of another module:
.serif-font { font-family: Georgia, serif;}.display { composes: serif-font; font-size: 30px; line-height: 35px;}
Applied to elements can be used like this:
import type from "./type.css";element.innerHTML = `
After that, the compiled template might be something like this:
Introduced from another module, you can write this:
.element { composes: dark-red from "./colors.css"; font-size: 30px; line-height: 1.2;}
- Solve the problem of nesting level too deep
Because the CSS modules only focuses on the component itself, the component itself can be written using a flat class name, similar to this:
.root { composes: box from "shared/styles/layout.css"; border-style: dotted; border-color: green;}.text { composes: heading from "shared/styles/typography.css"; font-weight: 200; color: green;}
How to use CSS Modules CSS modules is not limited to which front-end library you use, whether it's react, vue, or angular, as long as you can use the build tool to compile and package.
Below I use webpack
as an example, step by step introduce CSS Modules.
Build the most initial application .├── build│ └── bundle.js├── index.html├── node_modules├── package-lock.json├── package.json├── src│ ├── index.js│ └── styles└── webpack.config.js
Index.js as a program entry, the Styles folder stores style files, with Webpack.config.js as the Webpack configuration file.
// index.jsvar html = `<div class="header">
Style file:
/* global.css */* { margin: 0; padding: 0;}.container { padding: 20px;}/* index.css */.header { font-size: 32px;}.title { border-bottom: 1px solid #ccc; padding-bottom: 20px;}
Template file:
<!-- index.html --><!DOCTYPE html>
Global installation dependency, configuration execution script:
npm install webpack webpack-cli --save-dev
Package.json
"scripts": { "build": "npx webpack && open index.html"}
In the console execution npm run build
, the result is:
> css-modules-demo@1.0.0 build /Users/yhhu/Documents/coding/css-modules-demo> npx webpack && open index.htmlHash: 5810d2ecd760c08cc078Version: webpack 4.17.1Time: 78msBuilt at: 2018-08-26 15:09:31 Asset Size Chunks Chunk Namesbundle.js 3.97 KiB main [emitted] mainEntrypoint main = bundle.js[./src/index.js] 196 bytes {main} [built]
adding Styles and Loaders Package.json the ability to work with CSS in Loader
module: { rules: [ { test: /\.js/, loader: 'babel-loader', include: __dirname + '/src', exclude: __dirname + '/src/styles' }, { test: /\.css$/, use: [ { loader: 'style-loader' }, { loader: 'css-loader', options: { } } ] } ] }
Two CSS files introduced in Index.js
// index.jsimport './styles/global.css'import './styles/index.css'const html = `<div class="header">
After compiling, the result is:
displayed in the browser as:
Extract public styles You can see build
that there is only one in the directory after packaging bundle.js
, we are now going to extract the style file
./build/└── bundle.js
- Installation dependencies
npm install --save-dev mini-css-extract-plugin
var MiniCssExtractPlugin = require("mini-css-extract-plugin");modules: { rules: [ // { // test: /\.css$/, // use: [ // { loader: "style-loader" }, // { // loader: "css-loader", // options: { // } // } // ] // }, { test: /\.css$/, use: [ { loader: MiniCssExtractPlugin.loader, options: { publicPath: './build/styles' } }, { loader: "css-loader", options: { } } ] } ]},plugins: [ new MiniCssExtractPlugin({ filename: "[name].css", chunkFilename: "[id].css" })],
- Introduce a style file in a template
<!-- index.html --><!DOCTYPE html>
You can see that there is a main.css
build
Turn on CSS modules features The default in the css-loader
is not open css modules
function, to open can be set modules: true
, more can see the official css-loader
use method of modification webpack.config.js
, as follows:
{ test: /\.css$/, use: [ { loader: MiniCssExtractPlugin.loader, options: { publicPath: './build/styles' } }, { loader: "css-loader", options: { modules: true } } ]}
index.js
to modify the references in a file:
import './styles/global.css'import Index from './styles/index.css'const html = `<div class=${Index.header}>
As you can see, it's import
a direct file, and now it's a css
form of exporting an object, and we can Index
print out the object and see what it is:
Directly corresponds to the way we refer, and then we look at main.css
what is generated in particular:
* { margin: 0; padding: 0;}._2BQ9qrIFipNbLIGEytIz5Q { padding: 20px;}._3Ukt9LHwDhphmidalfey-S { font-size: 32px;}._3XpLkKvmw0hNfJyl8yU3i4 { border-bottom: 1px solid #ccc; padding-bottom: 20px;}
After synthesizing a file, all class names are hashed, so that the class name is unique, so let's look at inspector
the style app in the browser as follows:
In fact, the container
style we do not need to convert, because I have fixed it to die on the container, then what should we do?
Global scope To think of a class name that does not need to be loaded, you can use it :global(className)
to wrap it, so the class will not be converted, it will be output as is, and we'll modifyglobal.css
/* global.css */* { margin: 0; padding: 0;}:global(.container) { padding: 20px;}
We'll take a look.main.css
You can see that the .container
class has not been converted
Defining a Hash class name CSS modules By default is [Hash:base64] for the class name conversion, the level of recognition is not high, so we need to customize
To turn on customizations, you can use a configuration parameter localIdentName
that is configured as follows:
{ loader: "css-loader", options: { modules: true, localIdentName: '[path][name]__[local]--[hash:base64:5]' }}
Class name combination Sass
What do we need to do if we implement a similar inheritance function? The keyword in CSS modules composes
allows us to inherit another class, modified index.css
as follows:
.red { color: red;}.header { font-size: 32px;}.title { composes: red; border-bottom: 1px solid #ccc; padding-bottom: 20px;}
We have added a red
class name that title
implements inheritance in, and the result after compilation is:
Found one more src-styles-index__red--1ihPk
class name, which is the class we inherited above
In addition to inheriting from our own modules, we can also inherit CSS rules from other files, as follows:
We'll styles
create a new folder under thecolor.css
/* color.css */.red { color: red;}.blue { color: blue;}
And then index.css
import it into the file.
/* index.css */.red { color: red;}.header { font-size: 32px;}.title { color: green; composes: blue from './color.css'; composes: red; border-bottom: 1px solid #ccc; padding-bottom: 20px;}
Finally, we will find that the color of the text is green, it can be seen that its own module declaration of the highest priority, if the self-declared to color
remove, then the introduction of itself and from other documents introduced by the same declaration how to display it?
The answer is that the claims introduced by itself will have a higher priority.
Summarize At this point, all the CSS modules usage has been introduced, as for the follow-up and how to apply React
, Vue
and Angular
in, I believe that the above content can know how to write, how to use with the preprocessor to believe that the problem is not big.
Finally, the Code Warehouse library for this article is: Github.com/rynxiao/css-modules-demo
Reference links
- CSS modules-solving the challenges of CSS at scale
- GitHub repo
- What is CSS Modules and why do we need them?
- Getting Started with CSS Modules
- Get BEM
- CSS Modules Usage Tutorial
- CSS modules using the detailed
- Explore CSS parsing principles