目录
CSS存在的问题
一、类名冲突
当一个项目的模块和界面变动越来越丰富的时候,命名一个css类名难度越来越大,因为新的类名很可能会在其他模块存在,但是我们并没发现,这样的情况下,会导致css类名冲突,从而所写的样式会影响到其他模块。一般这样的情况下我们都会选择通过css选择器或者嵌套来选择元素,保证不会影响到其他模块,但是这样依然会有影响其他模块的风险,同时通过选择器和层级来选择的元素往往要写很长一段css选择的代码,这样很不容易阅读,并且难以维护和修改。
如下:index.css和block.css的.a 类名冲突
//index.css:
.a{}
.b{}
.b .c .d >.e{}
//block.css
.a{}
.b .e{}
二、样式或属性值重复难以维护
在一个网站里面,相同的样式和属性值是非常多的。如color和其色值,border的样式,在一个网站里复用的概率非常大。这些相同的样式如果要全部修改的话,需要修改多出地方,会变得如果非常难以维护。
如下,color和border重复很多,如要修改其中的颜色,要改很多个地方,非常麻烦。
body{
background-color:#000
}
.a{
color:#000
}
.b{
border:1px solid #000
color:#000
}
.c{
border:1px solid #000
background-color:#000
}
如何解决CSS存在的问题
一、解决类名冲突
方式一:使用命名规范约束css类名,来保证css类名的唯一性。常见的命名标准有:OOCSS,ACSS,BEM,SMACSS等。
方式二:用js来为元素对象添加css样式。由于此方法是等同于在元素行内直接写style样式,取消了通过css类名来书写样式,从而避免了类名冲突。例如:
html:
<div class="oDiv"></div>
js:
var div = document.getElementsByClassName("oDiv")[0]
//添加样式
div.style.border = "1px solid black"
此方式的缺点:
1.样式多的时候,会影响html代码阅读。因为通过js给元素对象添加样式,是添加在元素的行内,如果样式一多的话,会导致整个元素标签很长都是样式,不易阅读。
2.如果全部都用这种方式去书写css的话,要在页面修改样式也很不方便。
方式三:css模块化。此方式需要结合webpack和css-loader等工具配合。可以
在css中任意书写类名,当webpack构建css发现了类名冲突的时候,会对冲突的css类名进行转换,来保证css类名的唯一性。
二、解决样式或属性值重复难以维护
方案:使用CSS预编译工具
CSS预编译工具是社区团队对css语言问题的一种解决方案。它类似于编程语言:支持变量、函数等高级语法,可以对样式进行封装,优化选择元素。最后经过编译器将其代码编译成浏览器能识别的CSS代码。
常见的CSS预编译器:
- less
- sass
BEM命名规范
官方对BEM的原理:
BEM解决这一问题的思路在于,由于项目开发中,每个组件都是唯一无二的,其名字也是独一无二的,组件内部元素的名字都加上组件名,并用元素的名字作为选择器,自然组件内的样式就不会与组件外的样式冲突了。
这是通过组件名的唯一性来保证选择器的唯一性,从而保证样式不会污染到组件外。
这也可以看作是一种“硬性约束”,因为一般来说,我们的组件会放置在同一目录下,那么操作系统中,同一目录下文件名必须唯一,这一点也就确保了组件之间不会冲突。
BEM的命名规矩很容易记:block-name__element-name--modifier-name,也就是模块名 + 元素名 + 修饰器名。
如,在app/component/footer.html
<div class="footer">
<div class="footer__content">
<ul class="footer__parners">
<li class="footer__parner-child"></li>
<li class="footer__parner-child"></li>
<li class="footer__parner-child footer__parner-child--active"></li>
</ul>
</div>
<p class="footer__desc">xxx</p>
<div>
footer.css
.footer{margin:0;pading:0}
.footer__content{height:40px}
.footer__parners{float:left}
.footer__parner-child{marging:5px}
.footer__parner-child--active{backgorund-color:#000}
.footer__desc{font-weight:600}
更多细节参考BEM官网:https://www.bemcss.com/
css模块化
原理:webpack利用CSS-loader读取模块文件后,一旦发现CSS类名冲突,便会把冲突的CSS类名转换成唯一性的hash字符串(通过hash加密模块路径和类名),从而保证了CSS类名的唯一性。
css-loader
安装css-loader:
npm i css-loader -D
配置webpack.config.js中的loader
module: {
rules: [
{
test: /\.css$/i,
use: [
{
loader:'css-loader'
},
],
},
],
},
案例:
./src/index.js:
import indexCss from './css/main.css'
import blockACss from './css/block-a.css'
console.log("indexCss",indexCss)
console.log("blockACss",blockACss)
./src/css/main.css:
body{
background: yellow;
font-size: 20px;
}
.a{
color: red;
}
./src/css/block-a.css:
body{
background-color: black;
}
.a{
color: blue;
}
在浏览器运行打包后的./dist/main.js:
分析:
main.css和block-a.css两个文件中.a 这个CSS类名冲突了,因此被转化hash字符串保证唯一性。
控制css-loader对类名的转换:
全局的、静态、不需要进行转换的类名,如下设置.base为全局,css-loader便不会对其转化:
:global(.base){ ... }
配置默认使用了local
:local(.base){ ... }
等于
.base{ ... }
案例:
./src/index.js:
import indexCss from './css/index.css'
import aCss from './css/a.css'
console.log("indexCss",indexCss)
console.log('aCss',aCss)
'./src/css/index.css'
:global(.base){
background-color: red;
}
'./src/css/a.css'
:local(.base){
background-color: red;
}
结果:
可以看出.base设置了:global没有被转化。
css-loader模块化使用注意项
- css-loader会处理类名选择器和id,不处理其他选择器
- 使用css-loader没必要使用其他命名规范
- 使用css-loader尽量只使用顶级类名,不避免使用嵌套,容易阅读也方便webpack对代码的优化。
style-loader
如何应用转换后的CSS类到页面? 需要通过使用style-loader,把转换后的css样式都写入到页面。
安装style-loader
npm i style-loader -D
webpack.config.js中配置loader:
module:{
rules: [
{
test: /\.css$/i,
use: [
"style-loader",
{
loader:"css-loader",
options:{
modules:true
}
}
],
},
],
}
案例:
./src/index.js:
import indexCss from './css/index.css'
import aCss from './css/a.css'
console.log("indexCss",indexCss)
console.log('aCss',aCss)
./src/css/index.css:
.tab{
color:red;
font-size: 16px;
}
.footer{
padding: 0;
}
./src/css/a.css:
.tab{
color: blue;
}
.top{
margin: 0;
}
在浏览器运行打包后的./dist/main.js:
分析:
从以上打印输出的结果可以看出,对象中的键名就是我们所写的css名,而其键值就是hash转换后的Css类名。因此为dom元素添加对应类名,即可获得对应样式。
import indexCss from './css/index.css'
import aCss from './css/a.css'
console.log("indexCss",indexCss)
console.log('aCss',aCss)
//添加dom元素
var oTab = document.getElementById('#tab')
var oTop = document.getElementById('#top')
var oFooter = document.getElementsByName("#footer")
//添加class类名
oTab.className = aCss.tab
oTop.className = aCss.top
oFooter.className = indexCss.footer