模块化的CSS
今天主要介绍一下模块化的CSS思想。随着前端技术的发展,javascript模块化已经得到广泛运用,无论是CMD规范还是ES6的模块系统也好,都认可了模块化开发的优势。
那么CSS的模块化也是未来的一种趋势。
什么是CSS模块化,为什么CSS模块化才是未来呢?
局部
在CSS模块化思想中,每个文件被编译成独立的文件。这样我们就只需要使用简单通用的类选择器名字就好了,我们不再需要担心它会污染全局的变量。
比如一个拥有四个状态的按钮,在以前,我们可能会这样命名:
/* 组件/submit-button.css */
.Button { /* all styles for Normal */ }
.Button--disabled { /* overrides for Disabled */ }
.Button--error { /* overrides for Error */ }
.Button--in-progress { /* overrides for In Progress */
是使用的时候:
<button class="Button Button--in-progress">Processing...</button>
这是Bootstrap中的命名方式,也是目前CSS能够给出的最好的解决办法。
但是在模块化CSS中,我们再也不必为名字不独特而担心了:
/* 组件/submit-button.css */
.normal { /* all styles for Normal */ }
.disabled { /* all styles for Disabled */ }
.error { /* all styles for Error */ }
.inProgress { /* all styles for In Progress */
整个文件已经被命名为submit-button.css,我们可以通过import或者require从js中导入文件,使得CSS模块被编译成为可能:
/* 组件/submit-button.js */
import styles from './submit-button.css';
buttonElem.outerHTML = `<button class=${styles.normal}>Submit</button>`
这里对于模板字符串$用法不理解的同学可以看看ES6的新特性,这里就不做详细介绍了。
因为编译后的实际类名会自动生成并保证是唯一的,所以最终程序可能是如下样子:
<button class="components_submit_button__normal__abc5436">
Processing...
</button>
组件
CSS模块化中必不可少的就是组件,就像刚才所说的,按钮的样式对比如下:
/* BEM 风格*/
innerHTML = `<button class="Button Button--in-progress">`
/* CSS 模块化*/
innerHTML = `<button class="${styles.inProgress}">`
这样就带来了一个问题,如果我们要给一个对象赋予多个CSS呢?这就要用到组件:
.common {
/* 这里填写基本样式 */
}
.normal {
composes: common;
/* Normal样式 */
}
.disabled {
composes: common;
/* Disabled样式 */
}
.error {
composes: common;
/* Error样式 */
}
.inProgress {
composes: common;
/* In Progress样式 */
}
如果用过CSS预编译工具如Less或Sass的话,可以理解为和extends语法类似。但是其工作原理是不同的,因为Less或Sass会将.common中的内容丢到.normal中去,而composes会将两个类同时赋予DOM结点,比如:
.common { /* font-sizes, padding, border-radius */ }
.normal { composes: common; /* blue color, light blue background */ }
.error { composes: common; /* red color, light red background */ }
编译结果为:
.components_submit_button__common__abc5436 { /* font-sizes, padding, border-radius */ }
.components_submit_button__normal__def6547 { /* blue color, light blue background */ }
.components_submit_button__error__1638bcd { /* red color, light red background */ }
在html页面中效果为:
<button class="components_submit_button__common__abc5436 components_submit_button__normal__def6547">
Submit
</button>
也就是说.common和.normal同时修饰button结点。
共享
就像javascript模块化中通过import或者require来加载依赖一样,CSS通过compose来从另外一个文件加载:
/* colors.css */
.primary {
color: #720;
}
.secondary {
color: #777;
}
/* other helper classes... */
submit-button组件想载入color组件的内容:
/* submit-button.css */
.common { /* font-sizes, padding, border-radius */ }
.normal {
composes: common;
composes: primary from "../shared/colors.css";
}
复用
在没有CSS模块化之前,我们常常会遇到这样一个问题,就是一个元素中某些属性没有复用性:
.some_element {
font-size: 1.5rem;
color: rgba(0,0,0,0);
padding: 0.5rem;
box-shadow: 0 0 4px -2px;
}
以前我们可以通过Less或者Sass来处理,现在我们可以通过使用组件:
.element {
composes: large from "./typography.css";
composes: dark-text from "./colors.css";
composes: padding-all-medium from "./layout.css";
composes: subtle-shadow from "./effect.css";
}
将不同类型的属性放到不同的模块中,从而进行拼装组合来进行复用。