CSS模块化--解决命名冲突等问题
CSS模块化方案
原因:CSS作用域是全局的,项目越来越大,人越来越多,命名慢慢成为了问题,于是CSS 社区也诞生了相应的模块化解决方案:BEM、Atomic CSS、OOCSS、SMACSS、ITCSS,以及 CSS Modules 和 CSS-in-JS 等。
根据这些 CSS 模块化方案的特点,简单的分为三大类:
- CSS 命名方法论:通过人工的方式来约定命名规则。
- CSS Modules:一个 CSS 文件就是一个独立的模块。
- CSS-in-JS:在 JS 中写 CSS。
CSS 命名方法论
BEM
BEM即为块级元素修饰字符(Block Element Modifier),以 .block__element--modifier 形式命名,即 .模块名__元素名--修饰符名
三个部分,用双下划线 __ 来明确区分模块名和元素名,用双横线 -- 来明确区分元素名和修饰符名。
BEM 的命名规范如下:
/* 块即是通常所说的 Web 应用开发中的组件或模块。每个块在逻辑上和功能上都是相互独立的。 */
.block {}
.card {}
/* 元素是块中的组成部分。元素不能离开块来使用。BEM 不推荐在元素中嵌套其他元素。 */
.block__element {}
.card__head {}
.card__menu {}
.card__menu-item {}
/* 修饰符用来定义块或元素的外观和行为。同样的块在应用不同的修饰符之后,会有不同的外观 */
.block--modifier {}
.card__menu-item--active {}
.card__menu-item--disable {}
通过 BEM 的命名方式严格控制CSS命名去解决命名冲突的问题主要还是人为去遵守命名约束,那么就会有出错的时候。
Atomic CSS
Atomic CSS是原子化的CSS,就是将单一的属性封装一个Class,保证没有重复的样式,这样便于属性的复用,同时也会导致样式复杂的元素Class非常臃肿,所以具体的使用还是需要根据开发场景判断。
例如:
<div class="mt-10 w-100 h-15"></div>
// mt-10 > margin-top: 10px;
// w-100 > width: 100px;
// h-15 > height: 15px;
Tailwind CSS是使用Atomic CSS的一种代表框架,感兴趣大家可以自己实践下。 www.tailwindcss.cn/
CSS Modules
CSS Modules 是用于模块化和组合 CSS 的系统,通过加入局部作用域、依赖管理来避免全局污染和样式冲突,并且 CSS Modules 在打包的时候会自动将类名转换成 hash 值,完全杜绝 css 类名冲突的问题。
特性:
- 作用域:模块中的名称默认都属于本地作用域,定义在
:local
中的名称也属于本地作用域,定义在:global
中的名称属于全局作用域,全局名称不会被编译成哈希字符串。 - 命名:对于本地类名称,CSS Modules 建议使用
camelCase
方式来命名,这样会使 JS 文件更干净,即styles.className
。 但是你仍然可以固执己见地使用styles['class-name']
,允许但不提倡。 - 组合:使用
composes
属性来继承另一个选择器的样式,这与Sass
的@extend
规则类似。 - 变量:使用
@value
来定义变量,不过需要安装PostCSS
和postcss-modules-values
插件。
启用 CSS Modules
CSS Modules 很容易学。webpack 自带的 css-loader
组件,自带了 CSS Modules,通过简单的配置即可使用。
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
modules: true,
}
}
],
/* 或者这么写
use:[
loader: "style-loader!css-loader?modules"
]
*/
},
]
}
};
自定义生成的类名
css-loader
默认的哈希算法是[hash:base64]
,但是我们可能有需求去自定义想要的类名格式,css-loader
为我们提供了localIdentName
参数指定生成的名字格式。
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
modules: true,
localIdentName: '[name]__[local]-[hash:base64:5]',
}
}
],
/* 或者这么写
use:[
loader: "style-loader!css-loader?modules&localIdentName=[path][name]---[local]---[hash:base64:5]"
]
*/
},
]
}
};
作用域
CSS Modules 通过默认的局部作用域 :local
来实现样式的局部化,使用全局作用域 :global
来实现样式的全局化,同时全局的类名打包不会被编译成哈希字符串。
// 局部样式(可以省略:local)
.normal {
color: green;
}
:local(.normal) {
color: green;
}
// 全局样式
:global(.btn) {
color: red;
}
然后我们在js中使用就先导入css文件,然后通过导入对象的属性名使用样式
// 导入css
import styles from './Button.css';
console.log(styles);
// 使用normal样式
buttonElem.outerHTML = `<button class=${styles.normal}>Submit</button>`
Compose 组合样式
为了避免代码的冗余,我们需要去把一些样式去复用,而 CSS Modules 也提供了相应的方法 composes
去让我们处理。
/* components/Button.css */
.common { /* 所有通用的样式 */ }
.normal {
composes: common;
/* normal 其它样式 */
}
.disabled {
composes: common;
// 导入别的css文件
composes: shadow from './author.css';
/* disabled 其它样式 */
}
import styles from './Button.css';
buttonElem.outerHTML = `<button class=${styles.normal}>Submit</button>`
变量
@value blue: #0c77f8;
@value red: #ff0000;
@value green: #aaf200;
.title{
color:red;
background-color:blue;
}
CSS in JS
正如我们字面看到的意思,CSS in JS就是在JavaScript中写css,而不是单独的css文件去管理。在JS中使用了,那么 css 中使用一些属于 JS 的如模块声明、变量定义、函数调用和条件判断等语言特性来提供灵活的可扩展的样式定义。
CSS in JS 在react社区的热度是最高的,因为 react 本身不会管用户怎么去为组件定义样式问题,而vue有属于框架自己的一套定义样式的方案。
CSS in JS 目前已经有超过 40 多种方案的实现,最出名的是 styled-components。
组件内写法
// ======== Basic.jsx文件 ==========
import React, { Component } from 'react';
// 导入样式组件
import styled from 'styled-components';
class Basic extends Component {
render() {
return (
<div>
<Ha>内部文件写法</Ha>
</div>
);
}
}
const Ha = styled.div`
font-size: 50px;
color: red;
background: pink;
width: 100%;
height: 100vh;
`
export default Basic;
导入写法
// ======== Card.jsx文件 ==========
// 导入样式组件
import { CardText } from './style';
function Card() {
return (
<div className="card">
Card卡片
<CardText>外部Card卡片写法</CardText>
</div>
);
}
export default Card;
// ======== style.js文件 ==========
// ======== 外部写法(导入样式组件) ========
import styled from 'styled-components';
// const 标签名(首字母大写)= styled.HTML标签名`css样式`
// 导出
export const CardText = styled.div`
font-size: 50px;
font-family: 华文行楷;
color: orange;
background-color: blue;
width: 40vw;
`
样式继承
在styled-components中也可以使用样式的继承,有继承和重载两种情况
// ======== style1.js文件 ==========
// ======== 样式继承 ========
import styled from 'styled-components';
// const 标签名(首字母大写)= styled.HTML标签名`css样式`
// 导出
const Fu = styled.div`
font-size: 50px;
font-family:'Courier New',Courier,monospace,kai;
color: green;
width: 30vw;
`
// 子继承父
// 继承:color 和 font-family子没有,会用父的
// 重载:font-size两者都有,以子为准
const Zi = styled(Fu)`
font-size: 80px;
background: yellowgreen;
width: 30vw;
`
export { Fu, Zi };
属性传递
在styled-components中可以使用动态传参的方式对部分需要变化的样式进行控制
// ======== ChuanDi.jsx文件 ==========
import React, { Component } from 'react';
// 导入样式组件
import { ChuanDiStyle } from './style2';
class ChuanDi extends Component {
render() {
return (
<div>
<ChuanDiStyle>原先的样式</ChuanDiStyle>
<ChuanDiStyle bgColor={'yellow'} Size='30px'>传值的样式</ChuanDiStyle>
</div>
);
}
}
export default ChuanDi;
// ======== style2.js文件 ==========
// ======== 属性传递 ========
import styled from 'styled-components';
// 动态属性传递样式的值
// 没传值就是默认值green,传了值就是值的属性
const ChuanDiStyle = styled.div`
background: ${(props) => props.bgColor || 'red'};
font-size: ${(props) => props.Size || '60px'};
width: 20vw;
`
export { ChuanDiStyle };
参考文章
www.ruanyifeng.com/blog/2016/0… www.jianshu.com/p/9c7b0b36b…
转载自:https://juejin.cn/post/7082996568164401189