css工程化解决类名冲突、重复样式等问题
CSS工程化
在前端的不断发展,css也出现了很多问题,类名冲突、重复样式定义、css文件需要细分等问题。这篇文章我们来依次介绍如何解决这些问题的。
1、命名约定:BEM规范
BEM是一套针对css类样式的命名方法。
其他命名方法还有:OOCSS、AMCSS、SMACSS等等
BEM全称是:Block Element Modifier
一个完整的BEM类名:block__element_modifier
,例如: banner__dot_selected,可以表示:轮播图中,处于选中状态的小圆点
三个部分的具体含义为:
-
Block:页面中的大区域,表示最*的划分,例如:轮播图(
banner
)、布局(layout
)、文章(article
)等等 -
element:区域中的组成部分,例如:轮播图中的横幅图片(
banner__img
)、轮播图中的容器(banner__container
)、布局中的头部(layout__header
)、文章中的标题(article_title
) -
modifier:可选。通常表示状态,例如:处于展开状态的布局左边栏(
layout__left_expand
)、处于选中状态的轮播图小圆点(banner__dot_selected
)
在某些大型工程中,如果使用BEM命名法,还可能会增加一个前缀,来表示类名的用途,常见的前缀有:
- l: layout,表示这个样式是用于布局的
- c: component,表示这个样式是一个组件,即一个功能区域
- u: util,表示这个样式是一个通用的、工具性质的样式
- j: javascript,表示这个样式没有实际意义,是专门提供给js获取元素使用的
2、css in js
一看这个名字,我们应该差不多就能才出来,把css写在js中。它是把css变成js中的对象, 这样就可以完全运用js语言的优势。举个例子:
const styles = {
width: "400px",
height: "500px",
backgroundColor: "#ccc",
}
css变成对象,就完全不会出现命名冲突的问题。
css in js就有了一下几个优点:
- 绝无冲突的可能:由于它根本不存在类名,所以绝不可能出现类名冲突
- 更加灵活:可以充分利用JS语言灵活的特点,用各种招式来处理样式
- 应用面更广:只要支持js语言,就可以支持css in js,因此,在一些用JS语言开发移动端应用的时候非常好用,因为移动端应用很有可能并不支持css
但css in js同时也会出现一些问题:
- 书写不便:书写样式,特别是公共样式的时候,处理起来不是很方便
- 在页面中增加了大量冗余内容:在页面中处理css in js时,往往是将样式加入到元素的style属性中,会大量增加元素的内联样式,并且可能会有大量重复,不易阅读最终的页面代码
那么如何使用css in js,我们可以通过我们学过的各种技术将其应用到页面上,比如写个循环,使用框架等等方式,只要支持js,就可以使用。vue和react都支持css in js
3、css module
见名知意,css module就是把css写在不同的文件中,最后通过webpack构建工具合并成一个文件。多个不同的文件有相同的类名,合并之后没有冲突的类名。
在webpack中,我们使用css-loader来处理css文件,它就实现了css module的思想(css-loader使用在webpack常用插件中有讲述)。要启用css module,需要将css-loader的配置modules
设置为true
。
css module
原理非常简单,css-loader会将样式中的类名进行转换,转换为一个唯一的hash值。由于hash值是根据模块路径和类名生成的,因此,不同的css模块,哪怕具有相同的类名,转换后的hash值也不一样。
因此css-loader使用css module后,源代码的类名和最终生成的类名是不一样的,而开发者只知道自己写的源代码中的类名,并不知道最终的类名是什么,css-loader会导出二者的对应关系,但还包括了很多其他信息。而style-loader就是去除其他信息,仅暴露类名和对应生成的hash值
举个例子:
/*----------index.css----------*/
body{
background-color: #ccc;
}
h1{
color: #f40;
}
:global(.yellow){
color: yellow;
}
.red{
color: red;
}
/*----------index.js----------*/
import style from "./index.css"
console.log(style)
let h1 = document.getElementsByTagName("h1")[0]
h1.className = style.red
/*----------webpack.config.js----------*/
modules:{
rules[
{
test:/\.css$/,
use:["style-loader",
{
loader:"css-loader",
options:{modules:true}
}
]
}
]
}
解释:
- 全局类名:某些类名是全局的、静态的,不需要进行转换,仅需要在类名位置使用一个特殊的语法即可:
:global(.main){
...
}
- 局部类名:默认就是局部类名local,是可能会造成冲突的类名,会被css module进行转换
因为css-loader转换css代码后,交给style-loader进习性处理,sytle-loader是用一段js代码,将样式加到style文件中。而我们通常更需要的是生成一个css文件。于是就有了库mini-css-extract-plugin
,这个库提供了一个plugin和一个loader:
- plugin:负责生成css文件
- loader:负责记录要生成的css文件的内容,同时导出开启css-module后的样式对象
const MiniCssExtractPlugin = require("mini-css-extract-plugin")
module.exports = {
module: {
rules: [
{
test: /\.css$/, use: [MiniCssExtractPlugin.loader, "css-loader?modules"]
}
]
},
plugins: [
new MiniCssExtractPlugin({
filename:"[name].[hash:5].css"
}) //负责生成css文件,name是chunk的name
]
}
其中"css-loader?modules"
等同于
{
loader:"css-loader",
options:{
modules:true
}
}
4、预处理器
关于预处理器看这篇文章吧!十分钟带你学会Less预编译器
上一篇: vue.js生命周期
下一篇: Vue-cli (3.x)
推荐阅读