分享

【第584期】简单易懂的CSS Modules

 就这样了__ 2016-08-20

前言

本文是配置工程师需要的,涉及到webpack的内容。内容不会很复杂,读起来应该会相对比较轻松。本文由@acgtofe分享css modules


正文从这开始~


不要误会,CSS Modules可不是在说“css模块化这个好像在某些地方见过的词,它其实是特指一种近期才出现的技术手段。


什么技术手段呢?请待后文说明。


层叠样式表

我们知道,css的全名叫做层叠样式表,这个层叠到底是什么意思呢?

有一种解释是,如果你先写了一条样式规则(选手1):



然后又在后边写了一条类似的(选手2):


因为名字相同,选手2就会和选手1打起来(让你丫冒充我!)。结果是选手2获胜,class名为title的元素,最终的color值为gold


css里就像这样,随时可能一言不和就发生战争,结果输掉的一方就会被胜利的一方所覆盖。层叠一词可以说形象地描述了这个过程。


那么,为什么会有这样的层叠(zhàn zhēng )呢?


css的作用域问题

javascript里,可以做到这样的搭配:


利用javascript的函数作用域,两位同样名为title的选手可以友好相处。


但回到css里的样式规则,情况就完全不是这么回事了。


css不是程序语言,但如果说要给它加一个作用域的概念的话,那就是:只有全局作用域。


无论分拆为多少个css文件,无论用怎样的方式引入,所有的样式规则都位于同一作用域,只要选择符近似,就有发生覆盖的可能。


减少相互影响的策略

为减少相互影响,避免预料之外的样式覆盖,我们一直以来想过很多办法。


比如你接手一个别人留下来的旧项目,接下来要新增一个标题元素的时候,你会有意识地不去使用.title这样模糊的class名,因为它太容易重名了。最终,你用的名称可能是:



即使你决定要用.title这个名字,你也会加上包含选择符作为限定:


其中.module-1.module-2的名字应该是唯一的,这样的代码在组件化(模块化)的开发风格里很常见。


此外,一些有名的css理论,如SMACSS,会建议你为所有布局样式使用l-layout-的前缀,以示区分。


类似的做法还有很多,但归结起来,都是在尝试提供一种合理的命名约定。而合理的命名约定,的确是组织css代码的有效策略。


现在,我们有了新的可用策略,CSS Modules就是其中之一。


技术流的模块化

CSS Modules是一种技术流的组织css代码的策略,它将为css提供默认的局部作用域。


CSS Modules是如何做到的呢?来看一个CSSModules的简单例子吧。


有这样的一个html元素:



按照普通css的写法,我们可以这样为它添加样式:



现在我们改用CSS Modules。首先,css保持不变。然后,修改html的写法。不再这样直接写html,而是改为在javascript文件里动态添加,这样做(css文件名为main.css):



咦,require了一个css文件?对的,所以要用到webpack。编译后,htmlcss会变成这样:


看到这样不太美观的class名你大概就明白了,CSS Modules无法改变css全局作用域的本性,它是依靠动态生成class名这一手段,来实现局部作用域的。显然,这样的class名就可以是唯一的,不管原本的css代码写得有多随便,都可以这样转换得到不冲突的css代码。


模拟的局部作用域也没有关系,它是可靠的。


这个CSS Modules的例子说完了,但你一定跟我最初看到的时候一样有很多问题。


CSS Modules的应用细节

如何启用CSS Modules

“webpack编译css我也用过,怎么我用的时候不长这样?

一般来说,require一个css文件的写法是:


但在前面的例子中,用了var styles = require('./main.css');的写法。这就好像是在说,我要这个css文件里的样式是局部的,然后我根据需要自行取用。


在项目里应用CSS Modules有很多方法,目前比较常用的是使用webpackcss-loader。在webpack配置文件里写css-loader?modules就可以开启CSS Modules,例如前面的例子所用的:


才发现一直用着的css-loader原来有这功能?其实,CSS Modules确实是一个后来才并入css-loader的新功能。


自定义生成的class

名字都这样了,还怎么调试?

css-loader增加localIdentName参数,是可以指定生成的名字。localIdentName的默认值是[hash:base64],一般开发环境建议用类似这样的配置:



同样应用到前面的例子里,这时候就会变成这样的结果:


这样是不是要有意义多了?


如果是线上环境,可以考虑用更短的名字进一步减小css文件大小。


CSS Modules下的html

(看了前面例子里的el.outerHTML = ...后)

什么,outerHTMLclass名还要拼接?你家html才这么写呢!


很遗憾,CSS Modules官方的例子,也是这个意思:要使用CSS Modules,必须想办法把变量风格的class名注入到html中。也就是说,html模板系统是必需的,也正是如此,相比普通css的情况,CSS Moduleshtml写起来要更为费劲。


如果你搜一下CSS Modulesdemo,可以发现大部分都是基于React的。显然,虚拟DOM风格的React,搭配CSS Modules会很容易(ES6):


如果不使用React,还是那句话,只要有办法把变量风格的class名注入到html中,就可以用CSS Modules。原始的字符串拼接的写法显然很糟糕,但我们可以借助各种模板引擎和编译工具做一些改进。下面请看一个用Jade的参考示例。


想象一下你有一个用普通css的页面,但你想在一小块区域使用CSS Modules。这一块区域在一个容器元素里:



然后用jade来写html(关联的css文件为module_sp.css):



接下来,仍然是在javascript里添加这段jade生成的html



最后,记得在css-loader启用CSS Modules的同时,增加jade-loader


编译运行,就可以得到想要的结果。除Jade以外,还有些其他CSS Moduleshtml应用方案,推荐参考github上的这篇issue


目前CSS Modules还在发展中,而且也在考虑改进CSS Modules下的html写作体验。CSS Modules团队成员有提到一个叫CSS Modules Injector的未来规划项目,目的是让开发者不用javascript也可以使用CSS Modules(这就很接近原生html css的组合了)。


CSS Modules下的样式复用

样式都是唯一的了,怎么复用?

我们已经说了挺多普通css单个全局作用域的坏处。但对应的,这也有一个很大的好处,就是便于实现样式的复用。css理论OOCSS也是在追求这一点。


CSS Modules提供一个composes方法用于样式复用。例如,你有一个btn.css里有一条:



然后,你在另一个CSS Modulemodule_sp.css里可以这样引入它:



那么,这个div.btn-spDOM元素将会是:


可以看到,composes的用法比较类似sass@extend,但不同于@extend的是,composes并不增加css里的选择符总量,而是采用组合多个class名的形式。在这个例子里,原本仅有1classdiv.btn-sp,变成了2class


因此,CSS Modules建议只使用1class就定义好对应元素所需的全部样式。它们会再由CSS Modules转换为适当的class组合。


CSS Modules团队成员认为composesCSS Modules里最强大的功能:

For me, the most powerful idea in CSSModules is composition, where you can deconstruct your visual inventory intoatomic classes, and assemble them at a module level, without duplicating markupor hindering performance.

作者:@acgtofe

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多