首頁 > web前端 > css教學 > 主體

CSS Modules 詳解

Guanhui
發布: 2020-06-20 18:02:13
轉載
3777 人瀏覽過

CSS Modules 詳解

層疊樣式表

#我們知道,css的全名為做層疊樣式表,這個“層疊」到底是什麼意思呢?

有一種解釋是,如果你先寫了一條樣式規則(選手1):


#
.title {
 color: silver;
}
登入後複製

然後又在後邊寫了一條類似的(選手2):


.title {
 color: gold;
}
登入後複製

因為名字相同,選手2就會和選手1打起來(讓你女孩冒充我!)。結果是選手2獲勝,class名為title的元素,最終的color值為gold。

css裡面就像這樣,隨時可能一言不和就發生戰爭,結果輸掉的一方就會被勝利的一方所覆蓋。 「層疊」一詞可以說形像地描述了這個過程。

那麼,為什麼會有這樣的層疊(zhàn zhēng )呢?

css的作用域問題

在javascript裡,可以做到這樣的搭配:


var title = "silver";

(function(){
 var title = "gold";
 console.log(title); // gold
}());

console.log(title); // silver
登入後複製

利用javascript的函數作用域,兩位同樣名為title的選手可以友好相處。

但回到css裡的樣式規則,情況就完全不是這麼回事了。

css不是程式語言,但如果說要給它加一個作用域的概念的話,那就是:只有全域作用域。

無論分拆為多少個css文件,無論用怎樣的方式引入,所有的樣式規則都位於同一作用域,只要選擇符近似,就有發生覆蓋的可能。

減少相互影響的策略

為減少相互影響,避免預料之外的樣式覆蓋,我們一直以來想過很多辦法。

例如你接手一個別人留下來的舊項目,接下來要新增一個標題元素的時候,你會有意識地不去使用.title這樣模糊的class名,因為它太容易重名了。最終,你用的名稱可能是:


.module-sp-title {
 color: deepskyblue;
}
登入後複製

即使你決定要用.title這個名字,你也會加上包含選擇符作為限定:


.module-1 .title { 
 font-size: 18px;
}
/* ... */
.module-2 .title {
 font-size: 14px;
}
登入後複製

其中.module-1和.module-2的名字應該是唯一的,這樣的程式碼在元件化(模組化)的開發風格裡很常見。

此外,一些有名的css理論,如SMACSS,會建議你為所有佈局樣式使用l-或layout-的前綴,以示區分。

類似的做法還有很多,但歸結起來,都是在嘗試提供一個合理的命名約定。而合理的命名約定,的確是組織css代碼的有效策略。

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

技術流的模組化

CSS Modules是一種技術流的組織css程式碼的策略,它將為css提供預設的局部作用域。

CSS Modules是如何做到的呢?來看一個CSS Modules的簡單範例吧。

有這樣的一個html元素:


<h2 id="example_title" class="title">a title for CSS Modules</h2>
登入後複製

按照普通css的寫法,我們可以這樣為它添加樣式:


.title {
 background-color: snow;
}
登入後複製

現在我們改用CSS Modules。首先,css保持不變。然後,修改html的寫法。不再這樣直接寫html,而是改為在javascript檔案裡動態添加,這樣做(css檔案名為main.css):


var styles = require("./main.css");

var el = document.getElementById("example_title");
el.outerHTML = &#39;<h2 class="&#39; + styles.title + &#39;">a title for CSS Modules</h2>&#39;;
登入後複製

咦,require了一個css檔案?對的,所以要用到webpack。編譯後,html和css會變成這樣:

看到這樣不太美觀的class名稱你大概就明白了,CSS Modules無法改變css全域作用域的本性,它是依靠動態生成class名這一手段,來實現局部作用域的。顯然,這樣的class名稱就可以是唯一的,不管原本的css程式碼寫得有多隨便,都可以這樣轉換得到不衝突的css程式碼。

模擬的局部作用域也沒有關係,它是可靠的。

這個CSS Modules的例子說完了,但你一定跟我最初看到的時候一樣有很多問題。

CSS Modules的應用程式細節

#如何啟用CSS Modules

「webpack編譯css我也用過,怎麼我用的時候不長這樣?」

一般來說,require一個css檔的寫法是:


require("./main.css");
登入後複製

但在在前面的範例中,用了var styles = require("./main.css");的寫法。這就好像是在說,我要這個css檔案裡的樣式是局部的,然後我根據需要自行取用。

在專案裡應用CSS Modules有很多方法,目前比較常用的是使用webpack的css-loader。在webpack設定檔裡寫css-loader?modules就可以開啟CSS Modules,例如前面的範例所用的:


module: {
 loaders: [{
  test: /\.css$/,
  loader: &#39;style!css?modules&#39;
 }]
}
登入後複製

才發現一直用的css-loader原來有這功能?其實,CSS Modules確實是後來才併入css-loader的新功能。

自訂產生的class名稱

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

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


{
 test: /\.css$/,
 loader: &#39;style!css?modules&localIdentName=[name]__[local]___[hash:base64:5]&#39;
}
登入後複製

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

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

如果是线上环境,可以考虑用更短的名字进一步减小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):


import styles from &#39;./ScopedSelectors.css&#39;;

import React, { Component } from &#39;react&#39;;

export default class ScopedSelectors extends Component {

 render() {
 return (
  <p className={ styles.root }>
  <p className={ styles.text }>Scoped Selectors</p>
  </p>
 );
 }

};
登入後複製

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

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


<p id="module_sp_container"></p>
登入後複製

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


- styles = require("./module_sp.css");
h2(class=styles.title) a title for CSS Modules
登入後複製

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


var el = document.getElementById("module_sp_container");
var template = require("./main.jade");
el.innerHTML = template();
登入後複製

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


{
 test: /\.jade$/,
 loader: &#39;jade&#39;
}
登入後複製

编译运行,就可以得到想要的结果。除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里有一条:


.btn{
 display: inline-block;
}
登入後複製

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


.btn-sp{
 composes: btn from "./btn.css";
 font-size: 16px;
}
登入後複製

那么,这个p.btn-sp的DOM元素将会是:

可以看到,composes的用法比较类似sass的@extend,但不同于@extend的是,composes并不增加css里的选择符总量,而是采用组合多个class名的形式。在这个例子里,原本仅有1个class的p.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的输出:


var styles = require("./main.css");
console.log("styles = ", styles);
登入後複製

结果类似这样:


{
 "btn-sp": "_2SCQ7Kuv31NIIiVU-Q2ubA _2r6eZFEKnJgc7GLy11yRmV",
 title: "_1m-KkPQynpIso3ofWhMVuK"
}
登入後複製

这可以帮助理解CSS Modules是怎样工作的。

预编译器

sass等预编译器也可以用CSS Modules,对应的loader可能是这样:


{
 test: /\.scss$/,
 loader: &#39;style!css?modules!resolve-url!sass?sourceMap&#39;
}
登入後複製

注意不要因为是sass就习惯性地用嵌套写法,CSS Modules并不适合使用包含选择符。

建议的命名方式

CSS Modules会把.title转换为styles.title,由于后者是用在javascript中,因此驼峰命名会更适合。

如果像我之前那样写.btn-sp,需要注意在javascript中写为styles["btn-sp"]。

此外,你还可以为css-loader增加camelCase参数来实现自动转换:


{
 test: /\.css$/,
 loader: &#39;style!css?modules&camelCase&#39;,
}
登入後複製

这样即便你写.btn-sp,你也可以直接在javascript里用styles.btnSp。

结语

无论是一直以来我们认真遵循的命名约定,还是这个新的CSS Modules,目的都是一样的:可维护的css代码。我觉得就CSS Modules基本还是在写css这一点来说,它还是很友好的。

虽然本文为了严谨,结果写了相当长的篇幅,但希望你读过之后,还能觉得CSS Modules是简单易懂的。因为这样,我就达成我的目的:扣题,了。

推荐教程:《PHP


以上是CSS Modules 詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
css
來源:jb51.net
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板