目录
1. 为什么 CSS 要模块化?
1.1. 难以理解
1.2. 难以维护
2. 什么是CSS模块化?
3. CSS模块化方案——BEM
4. CSS模块化方案——CSS In JS
4.1. CSS-in-JS 库
4.2. styled-components 示例
5. CSS模块化方案——CSS Modules
1. 为什么 CSS 要模块化?
模块化 CSS 使用的主要场景是棘手的大规模 CSS。
写代码并不难,难的是在不让你的代码随着时间的推移成为拖累你的“技术债”。
1.1. 难以理解
以下是 CSS Guidelines 中的一个示例,这个示例展示了一个问题:除了写这段代码的人,没有人知道这段代码是干什么的。
<div class="box profile pro-user">
<img class="avatar image" />
<p class="bio">...</p>
</div>
光看代码无法回答这些问题,你必须在 CSS 代码中推理他们的作用。
1.2. 难以维护
大规模的 CSS 也难以维护。
barstool:n. 酒吧高脚凳 百度翻译
2. 什么是 CSS 模块化?
模块化 CSS 需要你换一个角度看问题,不从页面级别考虑,而是关注组成页面的小块。
这不是一个页面而是一个组件的集合。你会发现页面里包含的是 logo,搜索栏,导航,照片列表,辅助导航,标签框,视频播放器等。这些是可以网站的任何位置都可以独立使用的内容。它们只是碰巧在这个特定页面以这种方式组合。
3. CSS模块化方案——BEM
BEM,三个字母分别代表 Block、Element、Modifier,BEM 也是在 2009 年提出,起源于 Yandex(可以说是俄语版的 Google)。
BEM的核心概念是 —— 块(Block)由子元素(Element)构成,并且可以修改(Modified)。
BEM 命名约定:
.block-name__element--modifier
一个 BEM 例子:
<button class="btn btn--big btn--orange">
<span class="btn__price">$9.99</span>
<span class="btn__text">Subscribe</span>
</button>
即使不看 CSS 代码,就能看出这段代码会创建一个“大的”、“橙色”的价格按钮。
另一个 BEM 例子:
<section class="widget">
<h1 class="widget__header">Sterling Calculator</h1>
<form class="widget__form" action="process.php" method="post">
<p>Please enter an amount: (e.g. 92p, £2.12)</p>
<p>
<input name="amount"
class="widget__input widget__input--amount">
<input type="submit" value="Calculate"
class="widget__input widget__input--submit">
</p>
</form>
</section>
<style>
.widget {
background-color: #FC3;
}
.widget__header {
color: #930;
font-size: 3em;
margin-bottom: 0.3em;
text-shadow: #FFF 1px 1px 2px;
}
.widget__input {
border-radius: 5px;
font-size: 0.9em;
line-height: 1.3;
padding: 0.4em 0.7em;
}
.widget__input--amount {
border: 1px solid #930;
}
.widget__input--submit {
background-color: #EEE;
border: 0;
}
</style>
4. CSS模块化方案——CSS In JS
CSS-in-JS 是一种编程思想,即:用 JS 语言来写 CSS,而不是独立为一些 .css,.scss 或者 less 之类的文件,借助 JS 的语言特性来为 CSS 提供灵活、可扩展的样式定义。
4.1. CSS-in-JS 库
CSS-in-JS 有很多实现方案,常见的有:
4.2. styled-components 示例
import React from "react"
import ReactDOM from "react-dom"
import styled from 'styled-components'
function App() {
// Create a Title component that'll render an <h1> tag with some styles
const Title = styled.h1`
font-size: 1.5em;
text-align: center;
color: palevioletred;
`;
// Create a Wrapper component that'll render a <section> tag with some styles
const Wrapper = styled.section`
padding: 4em;
background: papayawhip;
`;
// Use Title and Wrapper like any other React component – except they're styled!
return <Wrapper>
<Title>
Hello World!
</Title>
</Wrapper>;
}
ReactDOM.render(<App/>, document.getElementById("app"));
5. CSS模块化方案——CSS Modules
A CSS Module is a CSS file in which all class names and animation names are scoped locally by default. https://github.com/css-modules/css-modules
Umijs 对 CSS Modules 特性的集成,非常人性化,值得学习:
Umi 会自动识别 CSS Modules 的使用,你把他当做 CSS Modules 用时才是 CSS Modules。 比如: // CSS Modules import styles from './foo.css'; // 非 CSS Modules import './foo.css'; https://umijs.org/docs/assets-css#css-modules
Umijs 的这个特性的实现原理为:
原理分析:
webpack.config.js:【oneOf + resourceQuery】
{
test: /\.css$/,
oneOf:[
{
resourceQuery: /modules/,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
hmr: mode === 'development' ? true : false
}
},
{
loader: 'css-loader',
options: {
modules: {
localIdentName: '[path][name]__[local]--[hash:base64:5]',
},
},
},
'postcss-loader'
]
},
{
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
hmr: mode === 'development' ? true : false
}
},
{
loader: 'css-loader',
},
'postcss-loader'
]
}
]
},
{
test: /\.less$/,
oneOf:[
{
resourceQuery: /modules/,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
hmr: mode === 'development' ? true : false
}
},
{
loader: 'css-loader',
options: {
modules: {
localIdentName: '[path][name]__[local]--[hash:base64:5]',
},
},
},
'postcss-loader',
{
loader: 'less-loader',
options: {
javascriptEnabled: true
}
}
]
},
{
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
hmr: mode === 'development' ? true : false
}
},
{
loader: 'css-loader',
},
'postcss-loader',
{
loader: 'less-loader',
options: {
javascriptEnabled: true
}
}
]
}
]
},
babel 插件:【自动添加 ?modules 后缀】
const {extname} = require('path');
const CSS_EXTNAMES = ['.css', '.less', '.sass', '.scss', '.stylus', '.styl'];
module.exports = function () {
return {
visitor: {
ImportDeclaration(path) {
const {specifiers, source} = path.node;
const {value} = source;
if (specifiers.length && CSS_EXTNAMES.includes(extname(value))) {
source.value = `${value}?modules`;
}
},
}
};
}
global.css:【全局样式-css】
.global {
font-size: 40px;
}
another-global.less:【全局样式-less】
.another-global {
font-size: 40px;
}
scoped.css:【局部样式-css】
.container {
padding: 4em;
background: papayawhip;
}
.title {
font-size: 1.5em;
text-align: center;
color: palevioletred;
}
another-scoped.less:【局部样式-less】
.container {
padding: 2em;
background: goldenrod;
}
.title {
font-size: 2em;
text-align: left;
color: palegoldenrod;
}
index.tsx:【程序入口】
import React from "react"
import ReactDOM from "react-dom"
import "./global.css"
import scoped from "./scoped.css"
import "./another-global.less"
import another_scoped from "./another-scoped.less"
function App() {
// Use Title and Wrapper like any other React component – except they're styled!
return <div>
<section className={scoped.container}>
<h1 className={scoped.title}>
Hello World!
</h1>
</section>
<section className={another_scoped.container}>
<h1 className={another_scoped.title}>
Hello World!
</h1>
</section>
</div>;
}
ReactDOM.render(<App/>, document.getElementById("app"));
运行结果:
观察 CSS Modules 是怎么处理局部样式的:
参考:
High-level advice and guidelines for writing sane, manageable, scalable CSS: https://cssguidelin.es/#naming-conventions-in-html Regarding CSS’s Global Scope: https://css-tricks.com/regarding-css-global-scope/?utm_medium=email&utm_source=mobilewebweekly What is Modular CSS? https://spaceninja.com/2018/09/18/what-is-modular-css/ -------------------------------------BEM: BEM: http://getbem.com/introduction/ Maintainable CSS with BEM: https://www.integralist.co.uk/posts/bem/#4 -------------------------------------CSS in JS: styled-components: https://styled-components.com/ -------------------------------------CSS Modules: CSS Modules: https://github.com/css-modules/css-modules css-loader: https://github.com/webpack-contrib/css-loader#modules umijs: https://umijs.org/docs/assets-css#css-modules --------------------------------------Webpack role.oneof: https://webpack.js.org/configuration/module/#ruleoneof role.resourceQuery: https://webpack.js.org/configuration/module/#ruleresourcequery css-loader: https://webpack.js.org/loaders/css-loader/#modules