• 售前

  • 售后

热门帖子
入门百科

简单明白带你了解CSS Modules

[复制链接]
啤酒瓶空了缓 显示全部楼层 发表于 2021-8-16 11:59:24 |阅读模式 打印 上一主题 下一主题
层叠样式表

我们知道,css的全名叫做层叠样式表,这个“层叠”到底是什么意思呢?
有一种解释是,如果你先写了一条样式规则(选手1):
  1. .title {
  2. color: silver;
  3. }
复制代码
然后又在后边写了一条类似的(选手2):
  1. .title {
  2. color: gold;
  3. }
复制代码
因为名字相同,选手2就会和选手1打起来(让你丫假冒我!)。结果是选手2得胜,class名为title的元素,最终的color值为gold。
css里就像如许,随时大概一言反面就发生战争,结果输掉的一方就会被胜利的一方所覆盖。“层叠”一词可以说形象地形貌了这个过程。
那么,为什么会有如许的层叠(zhàn zhēng )呢?
css的作用域题目

在javascript里,可以做到如许的搭配:
  1. var title = "silver";
  2. (function(){
  3. var title = "gold";
  4. console.log(title); // gold
  5. }());
  6. console.log(title); // silver
复制代码
利用javascript的函数作用域,两位同样名为title的选手可以友爱相处。
但回到css里的样式规则,情况就完全不是这么回事了。
css不是程序语言,但如果说要给它加一个作用域的概念的话,那就是:只有全局作用域。
无论分拆为多少个css文件,无论用怎样的方式引入,所有的样式规则都位于同一作用域,只要选择符近似,就有发生覆盖的大概。
淘汰相互影响的策略

为淘汰相互影响,制止预料之外的样式覆盖,我们一直以来想过很多办法。
好比你接手一个别人留下来的旧项目,接下来要新增一个标题元素的时间,你会故意识地不去利用.title如许模糊的class名,因为它太容易重名了。最终,你用的名称大概是:
  1. .module-sp-title {
  2. color: deepskyblue;
  3. }
复制代码
纵然你决定要用.title这个名字,你也会加上包罗选择符作为限定:
  1. .module-1 .title {
  2. font-size: 18px;
  3. }
  4. /* ... */
  5. .module-2 .title {
  6. font-size: 14px;
  7. }
复制代码
其中.module-1和.module-2的名字应该是唯一的,如许的代码在组件化(模块化)的开辟风格里很常见。
别的,一些著名的css理论,如SMACSS,会发起你为所有结构样式利用l-或layout-的前缀,以示区分。
类似的做法另有很多,但归结起来,都是在实验提供一种公道的命名约定。而公道的命名约定,简直是构造css代码的有效策略。
现在,我们有了新的可用策略,CSS Modules就是其中之一。
技能流的模块化

CSS Modules是一种技能流的构造css代码的策略,它将为css提供默认的局部作用域。
CSS Modules是如何做到的呢?来看一个CSS Modules的简单例子吧。
有如许的一个html元素:
  1. <h2 id="example_title" class="title">a title for CSS Modules</h2>
复制代码

按照平凡css的写法,我们可以如许为它添加样式:
  1. .title {
  2. background-color: snow;
  3. }
复制代码
现在我们改用CSS Modules。起首,css保持不变。然后,修改html的写法。不再如许直接写html,而是改为在javascript文件里动态添加,如许做(css文件名为main.css):
  1. var styles = require("./main.css");
  2. var el = document.getElementById("example_title");
  3. el.outerHTML = '<h2 class="' + styles.title + '">a title for CSS Modules</h2>';
复制代码
咦,require了一个css文件?对的,所以要用到webpack。编译后,html和css会变成如许:

看到如许不太雅观的class名你大概就明白了,CSS Modules无法改变css全局作用域的本性,它是依靠动态生成class名这一手段,来实现局部作用域的。显然,如许的class名就可以是唯一的,不管原本的css代码写得有多随便,都可以如许转换得到不辩论的css代码。
模仿的局部作用域也没有关系,它是可靠的。
这个CSS Modules的例子说完了,但你肯定跟我最初看到的时间一样有很多题目。
CSS Modules的应用细节

如何启用CSS Modules

“webpack编译css我也用过,怎么我用的时间不长如许?”
一样平常来说,require一个css文件的写法是:
  1. require("./main.css");
复制代码
但在前面的例子中,用了var styles = require("./main.css");的写法。这就似乎是在说,我要这个css文件里的样式是局部的,然后我根据须要自行取用。
在项目里应用CSS Modules有很多方法,现在比较常用的是利用webpack的css-loader。在webpack配置文件里写css-loader?modules就可以开启CSS Modules,比方前面的例子所用的:
  1. module: {
  2. loaders: [{
  3.   test: /\.css$/,
  4.   loader: 'style!css?modules'
  5. }]
  6. }
复制代码
才发现一直用着的css-loader原来有这功能?其实,CSS Modules确实是一个后来才并入css-loader的新功能。
自定义生成的class名

名字都如许了,还怎么调试?”
为css-loader增加localIdentName参数,是可以指定生成的名字。localIdentName的默认值是[hash:base64],一样平常开辟环境发起用类似如许的配置:
  1. {
  2. test: /\.css$/,
  3. loader: 'style!css?modules&localIdentName=[name]__[local]___[hash:base64:5]'
  4. }
复制代码
同样应用到前面的例子里,这时间就会变成如许的结果:

如许是不是要故意义多了?
如果是线上环境,可以思量用更短的名字进一步减小css文件巨细。
CSS Modules下的html

(看了前面例子里的el.outerHTML = ...后)
“什么,outerHTML?class名还要拼接?你家html才这么写呢!”
很遗憾,CSS Modules官方的例子,也是这个意思:要利用CSS Modules,必须想办法把变量风格的class名注入到html中。也就是说,html模板体系是必需的,也正是云云,相比平凡css的情况,CSS Modules的html写起来要更为费劲。
如果你搜一下CSS Modules的demo,可以发现大部分都是基于React的。显然,捏造DOM风格的React,搭配CSS Modules会很容易(ES6):
  1. import styles from './ScopedSelectors.css';
  2. import React, { Component } from 'react';
  3. export default class ScopedSelectors extends Component {
  4. render() {
  5. return (
  6.   <div className={ styles.root }>
  7.   <p className={ styles.text }>Scoped Selectors</p>
  8.   </div>
  9. );
  10. }
  11. };
复制代码
如果不利用React,照旧那句话,只要有办法把变量风格的class名注入到html中,就可以用CSS Modules。原始的字符串拼接的写法显然很糟糕,但我们可以借助各种模板引擎和编译工具做一些改进。下面请看一个用Jade的参考示例。
想象一下你有一个用平凡css的页面,但你想在一小块区域利用CSS Modules。这一块区域在一个容器元素里:
  1. <div id="module_sp_container"></div>
复制代码
后用jade来写html(关联的css文件为module_sp.css):
  1. - styles = require("./module_sp.css");
  2. h2(class=styles.title) a title for CSS Modules
复制代码
接下来,仍旧是在javascript里添加这段jade生成的html:
  1. var el = document.getElementById("module_sp_container");
  2. var template = require("./main.jade");
  3. el.innerHTML = template();
复制代码
末了,记得在css-loader启用CSS Modules的同时,增加jade-loader:
  1. {
  2. test: /\.jade$/,
  3. loader: 'jade'
  4. }
复制代码

编译运行,就可以得到想要的结果。除Jade以外,另有些其他CSS Modules的html应用方案,保举参考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里有一条:
  1. .btn{
  2. display: inline-block;
  3. }
复制代码
然后,你在另一个CSS Module的module_sp.css里可以如许引入它:
  1. .btn-sp{
  2. composes: btn from "./btn.css";
  3. font-size: 16px;
  4. }
复制代码
那么,这个div.btn-sp的DOM元素将会是:

可以看到,composes的用法比较类似sass的@extend,但不同于@extend的是,composes并不增加css里的选择符总量,而是接纳组合多个class名的情势。在这个例子里,原本仅有1个class的div.btn-sp,变成了2个class。
因此,CSS Modules发起只利用1个class就定义好对应元素所需的全部样式。它们会再由CSS Modules转换为得当的class组合。
CSS Modules团队成员以为composes是CSS Modules里最强大的功能:
For me, the most powerful idea in CSS Modules is composition, where you can deconstruct your visual inventory into atomic classes, and assemble them at a module level, without duplicating markup or hindering performance.
更具体的composes的用法及其理解,保举阅读CSS Modules: Welcome to the Future。
其他大概有效的补充

和已有的平凡css共存

很多项目会引入Bootstrap、Materialize等框架,它们是平凡的、全局的css。别的,你也大概自己会写一些平凡css。如何共存呢?CSS Modules团队成员对此提到过:
a CSS Module should only import information relative to it
意思是,发起把CSS Modules看做一种新的css,和原来的平凡css区分开来。好比,composes的时间,不要从那些平凡的css里去取。
在css-loader里通过指定test、include、exclude来区分它们。保持CSS Modules的纯净,只有想要应用CSS Modules的css文件,才启用CSS Modules。
只转换class和id

经过我自己的测试,CSS Modules只转换class和id,别的的标签选择符、伪类等都不会被转换。
发起只利用class。
一个CSS Module的输出

简单用console.log()就可以检察CSS Module的输出:
  1. var styles = require("./main.css");
  2. console.log("styles = ", styles);
复制代码
结果类似如许:
  1. {
  2. "btn-sp": "_2SCQ7Kuv31NIIiVU-Q2ubA _2r6eZFEKnJgc7GLy11yRmV",
  3. title: "_1m-KkPQynpIso3ofWhMVuK"
  4. }
复制代码

这可以资助理解CSS Modules是怎样工作的。
预编译器

sass等预编译器也可以用CSS Modules,对应的loader大概是如许:
  1. {
  2. test: /\.scss$/,
  3. loader: 'style!css?modules!resolve-url!sass?sourceMap'
  4. }
复制代码
注意不要因为是sass就习惯性地用嵌套写法,CSS Modules并不得当利用包罗选择符。
发起的命名方式

CSS Modules会把.title转换为styles.title,由于后者是用在javascript中,因此驼峰命名会更得当。
如果像我之前那样写.btn-sp,须要注意在javascript中写为styles["btn-sp"]。
别的,你还可以为css-loader增加camelCase参数来实现自动转换:
  1. {
  2. test: /\.css$/,
  3. loader: 'style!css?modules&camelCase',
  4. }
复制代码
如许即便你写.btn-sp,你也可以直接在javascript里用styles.btnSp。
结语

无论是一直以来我们认真遵照的命名约定,照旧这个新的CSS Modules,目标都是一样的:可维护的css代码。我觉得就CSS Modules根本照旧在写css这一点来说,它照旧很友爱的。
虽然本文为了严谨,结果写了相当长的篇幅,但盼望你读过之后,还能觉得CSS Modules是简单易懂的。因为如许,我就告竣我的目标:扣题,了。
以上就是本文的全部内容,盼望对各人的学习有所资助,也盼望各人多多支持草根技术分享。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x

帖子地址: 

回复

使用道具 举报

分享
推广
火星云矿 | 预约S19Pro,享500抵1000!
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

草根技术分享(草根吧)是全球知名中文IT技术交流平台,创建于2021年,包含原创博客、精品问答、职业培训、技术社区、资源下载等产品服务,提供原创、优质、完整内容的专业IT技术开发社区。
  • 官方手机版

  • 微信公众号

  • 商务合作