CSS Modules 的用法很简单,不过现阶段还需要 webpack 的支持。CSS Modules 的功能非常少,它一共就干了两件事:局部作用域和模块依赖。
项目路径
├── README.md
├── index.html
├── node_modules
│ └── ...
├── package.json
├── src
│ ├── animation.css
│ ├── colors.css
│ ├── index.js
│ └── style.css
└── webpack.config.js
把文件 clone 下来后,安装依赖,然后就可以运行了
$ git clone https://github.com/Leo555/css_modules_study.git
$ cd css_modules_study
$ npm install
$ npm run start
浏览器会自动打开静态文件,方便查看效果。
首先配置 webpack 环境(本文使用webpack2),给 css-loader 增加一个 modules 查询参数,表示打开 CSS Modules 功能。
简单的示例如下:
module: {
rules: [{
test: /\.css$/,
loader: "style-loader!css-loader?modules"
}]
},
如果需要给 CSS Modules 传递一些参数,可以用对象的方式:
module: {
rules: [{
test: /\.css$/,
use: ["style-loader",
{
loader: "css-loader",
options: {
modules: true,
localIdentName: '[path][name]__[local]--[hash:base64:5]',
importLoaders: 1,
camelCase: true
}
}]
}]
}
开启 CSS Modules 后,所有的 CSS 选择器都是局部作用域,除非声明它为全局的。
/*Scoped Selectors*/
.className {
color: green;
margin: 10px 0;
}
/*Global Selectors*/
:global(.text) {
font-size: 22px;
}
以上两个 CSS class 通过如下方法被 JS 引用
import styles from "./style.css";
// import { className } from "./style.css";
let app = document.getElementById('app')
app.innerHTML =
`
<div class="${styles.className}">Hello CSS Modules</div>
<div class="text">Global Selectors</div>
`
后面的引用方式都相同,因此略去,具体可查看 index.js。
查看构建后的 CSS,发现局部变量的名字被编译成 hash (localIdentName: '[path][name]__[local]--[hash:base64:5]'
),而全局变量的名字不变。
原来 CSS Modules 就做了这么一点微小的工作。
CSS Modules 通过组合的方式进行集成,以达成代码复用的效果。
/*Class Composes*/
.otherClassName {
composes: className;
width: 220px;
height: 60px;
line-height: 60px;
border-width: 2px;
border-style: solid;
}
/*Composes Overrides*/
.background {
border-radius: 20px;
background: #efefef;
composes: otherClassName;
border-style: dotted;
}
otherClassName 继承 className,因为拥有了 color 和 margin 属性,而 background 继承 otherClassName,却重写了 border-style。
在 animation.css 中,定义了动画 tada:
@keyframes tada {
from {
transform: scale3d(1, 1, 1);
}
10%, 20% {
transform: scale3d(.9, .9, .9) rotate3d(0, 0, 1, -3deg);
}
30%, 50%, 70%, 90% {
transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg);
}
40%, 60%, 80% {
transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg);
}
to {
transform: scale3d(1, 1, 1);
}
}
.tada {
animation: tada 2s infinite;
}
在 style 中的引用方式如下:
/*Scoped Animations*/
.backgroundAnimation {
composes: background;
composes: tada from './animation.css';
}
上面第二个 composes 也展示了如何从其它 CSS 模块中引用选择器。
通过 @value 定义变量和引用变量
colors.css
@value color: black;
@value fontSize: 22px;
引用方式
/*Define variables*/
@value colors: "./colors.css";
@value color, fontSize from colors;
.color {
composes: otherClassName;
color: color;
font-size: fontSize;
}
vue-loader 中集成了 CSS Modules,可以作为模拟 CSS 作用域的替代方案。
在 <style>
上添加 module 属性即可开启 CSS Modules 模式:
<style module>
.red {
color: red;
}
.bold {
font-weight: bold;
}
</style>
css-loader 会自动将生成的 CSS 对象注入到 $style 中,只需要在 <template>
中使用动态 class 绑定:
<template>
<p :class="$style.red">
This should be red
</p>
</template>
由于它是一个计算属性,它也适用于 :class
的 object/array 语法:
<template>
<div>
<p :class="{ [$style.red]: isRed }">
Am I red?
</p>
<p :class="[$style.red, $style.bold]">
Red and bold
</p>
</div>
</template>
在 JavaScript 访问它:
<script>
export default {
created () {
console.log(this.$style.red)
}
}
</script>
在 .vue 中可以定义不止一个 <style>
,为了避免被覆盖,你可以通过设置 module 属性来为它们定义注入后计算属性的名称。
<style module="a">
/* identifiers injected as a */
</style>
在前端模块化的道路上,CSS 显然落后 JS 非常多。ES2015/ES2016 的快速普及和 Babel/webpack 等工具的发展,让 JS 在大型项目工程化中越发强大,最终成为一流语言 。
CSS Modules 解决了哪些问题呢?
CSS 模块化的解决方案有很多,但主要有两类。一类是彻底抛弃 CSS,使用 JS 或 JSON 来写样式。Radium,jsxstyle,react-style 属于这一类。优点是能给 CSS 提供 JS 同样强大的模块化能力;缺点是不能利用成熟的 CSS 预处理器(或后处理器) Sass/Less/PostCSS,:hover 和 :active 伪类处理起来复杂。另一类是依旧使用 CSS,但使用 JS 来管理样式依赖,代表是 CSS Modules。CSS Modules 能最大化地结合现有 CSS 生态和 JS 模块化能力,API 简洁到几乎零学习成本。发布时依旧编译出单独的 JS 和 CSS。它并不依赖于 React,只要你使用 Webpack,可以在 Vue/Angular/jQuery 中使用。是我认为目前最好的 CSS 模块化解决方案。