最近在项目中使用React比较多,components
的组织方式使前端整个的开发思路更加清晰,在多人项目中,也会减少很多沟通的成本(需要什么组件,直接去找就可以了),但是由此也产生了一些困惑。
不过这里不会讨论React本身,只会讨论由Components方式引发的一些css写法的变化。
在我刚参加工作时,那时候前端开发还基本处在刀耕火种的阶段,加上我加入的第一家公司又不是一个以技术擅长的公司,所以当时前端开发工程师还被昵称为页面仔 (也就是只写页面样式,甚至很多时候是连js也不用写的)。
当时好像也没有听说过Bootstrap
这样的东西,更不用说less
,sass
这些东西,components
更是完全不知道是什么意思。全部 css 都是人肉写,class命名完全靠个人习惯,比如有人就很喜欢这样的命名方式:
.box {
...
}
.box .hd {
...
}
.bod .bd {
...
}
也有人喜欢这样的命名方式:
.box {
...
}
.box-hd {
...
}
.box-bd {
...
}
怎么说呢,这两种命名方式各有优劣,第一种结构更加清晰,在日后的各种可编译css语法(比如sass
)中用的基本都是这种写法。
但是也有问题,就是如果嵌套层级过多的话,会影响页面的渲染速度。
第二种呢,命名只有一层,所以css选择器可以很快的找到对应的样式,渲染速度会略快,但是写起来确实不太方便。
除了这两种写法各自的优劣之外,其实这两种方法还有个共同的弱点,就是样式容易被覆盖。
样式覆盖可以算 css 最主要的特性之一了,各种优先级的组合以及!important
的运用,可以衍生出各种灵活的样式变化。样式覆盖的特性是一把双刃剑,给予了css灵活性的同时也使覆盖bug几乎是无法避免的。
尤其是在维护一个老项目的时候,由于css本身很难以表达出完整的逻辑性,但样式的命名又是全局的,所以在修改样式时,很容易就不小心改动了或覆盖了本来正确的样式,相信很多人都有这种经历。到最后为了不影响之前的样式,只能不停的对要修改的部分进行覆盖,或者更干脆直接为要修改的部分重新定义样式。
这个问题一直困扰了我很长时间,直到有一天我看到了 facebook 页面上的class命名:
<div class="_1dwg">
<div class="_4r_y">
...
</div>
<div class="clearfix _5x46">
...
</div>
<div class="_5pb8 _5v9u _29h _303">
...
</div>
</div>
基本上所有的 className 都是一个混淆过的名字,在控制台里看也发现很少有样式一个接一个覆盖的问题。
className 的混淆可以理解为减少 className 命名的长度,减少网络请求的size(不过其实重点并不在这里)。同时多层的嵌套应该也会被混淆成为一个 className,从而大大减少命名冲突(最直观的体现就是央视被覆盖)的几率。这在大型项目中是很有用的,再不用担心自己的那条 className 不小心跟前人的有所重复,不小心就把样式给搞乱了。
再后来,前端开发逐渐进入components
阶段,从 css 到 scss ,样式模块的复用和 css 的逻辑性有了很大的提升,但是仍然没能解决我上面所说的问题。因为最终编译出来的还是 css 文件,所有的变量还是全局的,命名冲突还是会在所难免的发生。
直到后来我看到了这样一篇文章The End of Global CSS
在这篇文章里,提出了一个local scope的概念,大意就可以理解成给css代码添加了作用域,具体的实现就是将 className 编译为一个哈希串,从而避免了命名冲突(和facebook的做法很像)。
通过local scope
相当于给 css 添加了作用域的概念,从而可以笑消除全局的 className。看上去很美好,完美的解决了命名冲突的问题,但同时也使丢到了可覆盖带来的灵活性。但不论怎样,css 又离宇宙最好的语言进了一步。
为了解决上面所说的问题,在The End of Global CSS中,作者也提出了一些解决方案,例如使用:local
和:global
的方式来规定哪些 className 可以被暴露给全局对象,通过可预见的方式暴露一些变量到全局作用域中,虽然不是很完美,但是也算是一条路。(灵活和严谨本来就是一对矛盾,所以只能选择一条折中的方案)
说到这里,要说的基本已经说完了,再来说点题外话。
现在大家都用component
的方式进行开发,所谓component
的不仅仅是使用React
作为前端的模板,更重要的是所谓模块化的思路。关于模块化这里就不展开了,着重说一下模块化思路下的 css 开发。
通常情况下,不论使用 sass
或者less
或者其他的什么css语法,我们所做的都是
compiler => bundle.js + bundle.css
将 css 和 js 独立打包。
使用 webpack
的话,我们可以使用很多插件来完成这个工作。 例如:
css-loader
将 css 打包成独立的文件extract-text-webpack-plugin
自动生成当前component
同名的 css 文件css-loader
,将css和js打包成一体,形成CSS Modules的形式style
的方式引用(并不推荐这种方式)总而言之,到现在为止,css 开发早已脱离了当初的刀耕火种的年代,从可编译的less
到sass
,再到radium
和CSS Modules
的思路,css 的开发越来越工程化,不过到目前为止,还难以有一个令人十分满意的解决方案出现。
参考资料:
CSS Modules:
radium: