Development history of CSS:
- Writing native CSS rules, represented by
BEM
naming convention - Preprocessors like
Sass
,Less
, andStylus
- Postprocessors like
PostCSS
, plugin-based, such asautoprefix
CSS Modules
, used with build tools likewebpack
,Gulp
, orParcel
CSS in JS
, represented bystyled-component
based onReact
Native CSS rules are globally effective. To avoid style conflicts, many solutions have been developed.
BEM Naming Convention#
To avoid naming conflicts at the development level, make class names more meaningful, provide more description and clearer structure, and make CSS more readable, the BEM
(Block, Element, Modifier) front-end CSS naming convention was introduced by the Yandex team:
-
hyphen: used as a hyphen to connect multiple words of a block or sub-element__
double underscore: used to connect blocks and their sub-elements--
double hyphen: used to describe and modify the state or type of an element
/* Block represents a single component or module */
.article-detail {
display: flex;
}
/* Element is a component of the block */
.article-detail__button {
width: 120px;
height: 36px;
}
/* Modifier is used to describe and modify the state or type of an element */
.article-detail__button--primary {
color: #fff;
background-color: #3af;
}
However, the drawback is that the BEM
naming convention is tedious to write by hand, has low development efficiency, and is difficult to maintain.
CSS Modules#
CSS Modules
are essentially CSS files that cannot be used independently and need to be used with build tools. They provide many new features to native CSS, such as explicitly writing:
- Local and global CSS rules can be explicitly written
- Can be loaded and used in
JS
files as modules - Class names are converted to hash values during packaging to prevent CSS class name conflicts
/* style.css */
.className {
color: green;
background: red;
}
.otherClassName {
/* Supports style composition */
composes: className;
color: yellow;
}
.otherClassName {
/* Supports importing from other files */
composes: className from './style.css';
}
/* The above styles are by default locally scoped */
/* Local scope */
:local(p) {
color: #333;
}
/* Global scope */
:global(p) {
color: #333;
}
import styles from './style.css'
// import { className } from "./style.css";
element.innerHTML = '<div class="' + styles.className + '">'
// element.innerHTML = '<div class="' + styles['class-name'] + '">';
When used with webpack
, the configuration is as follows:
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: {
loader: 'css-loader',
options: {
modules: {
// Custom hash name, can use variables
localIdentName: '[path][name]__[local]--[hash:base64:5]'
}
}
}
}
]
}
}
The effect after packaging (class names are converted to custom hash name format):
._2DHwuiHWMnKTOYG45T0x34 {
color: red;
}
._10B-buq6_BEOTOl9urIjf8 {
background-color: blue;
}
Sass, Less, and Stylus#
To facilitate front-end developers in writing CSS, many preprocessors have been developed:
In general, they have the following features in addition to differences in syntax and certain features:
- Default local scope
- Support for nested rules
- Support for style composition and inheritance
- Support for importing external style files of the same preprocessor (
@import
) - Allow the use of variables and functions (color functions, etc.)
- Class names are hashed during compilation, avoiding conflicts
In addition to these, different preprocessors also have different features such as conditional statements and loop statements. Please refer to the documentation of the corresponding preprocessor for details.
One thing to note is that if the @import
imports a native CSS format file, it will generate additional http
requests.
On the other hand, if the @import
imports a file specific to the preprocessor, it is imported at the syntax and semantic level, rather than importing native CSS. It will be processed during compilation and only one CSS file will be generated, without increasing http
requests.
/* Importing native CSS will increase http requests */
@import 'reset.css';
/* Preprocessor format, less is the file extension of the corresponding preprocessor, such as scss, styl */
/* body.less */
body {
background: #eee;
}
/* style.less */
@import 'body.less';
PostCSS#
With the continuous development of front-end engineering, more and more tools have been developed to delegate repetitive work to build tools. In the CSS field, PostCSS
has emerged.
There is a saying about PostCSS
that I think is very true:
PostCSS
can be called theBabel
of the CSS world.
PostCSS
analyzes the syntax tree (AST) of CSS and processes the analysis results to complete a series of tasks that are considered tedious and complex by front-end developers. Common use cases include:
- Using language syntax validation tools to detect syntax errors, such as stylelint
- Transpiling and polyfilling CSS next-generation syntax rules
- Implementing specific functionality with plugins, such as automatically adding browser prefixes using
autoprefix
CSS Performance Optimization#
Common ways to optimize CSS performance:
- Reduce file splitting: Requesting multiple CSS files is subject to network constraints
- Reduce CSS nesting, recommended to not exceed three levels
- Remove unnecessary CSS selectors, use CSS selectors wisely, for example, no need to add other selectors before an id selector
- Reuse styles of similar elements, such as
button
,input
elements, etc. - Reduce the use of universal selectors (
*
) and attribute selectors ([name=nav]
), as these selectors usually require traversing all elements - Remove invalid and duplicate styles, for example, some properties have inheritance, so if the parent element defines them, the child elements do not need to set them again
- Separate common CSS rules between pages into separate CSS files, such as
normalize.css
, so that the browser only needs to load them once (cached for subsequent use) - Use CSS sprites to reduce the number of requests for images and icons
- Use CSS compression tools or project compilation and packaging tools to optimize CSS
- Reduce the use of
@import
, as it will generate additional requests and affect the loading order (except for CSS preprocessors) - Minimize frequent and significant layout reflows (window, element, text size changes, layout switching, size calculations, etc.) to reduce rendering overhead
- Avoid using
JS
to change individual CSS styles. If necessary, define CSS classes and change styles by changing class names - Reduce the use of complex and performance-demanding CSS animations