css模块化的解决方案
“ CSS模块”与W3C无关,而是建议的构建过程的一部分。 它会编译您的项目,重命名选择器和类,以使它们变得唯一,范围仅限于各个组件。 样式已锁定在这些组件中,除非您特别声明,否则不能在其他地方使用!
前言
如今,我们已经非常习惯于将网络技术作为应用程序背后的推动力。 网络应用,移动和桌面应用。 但是与简单的静态网站不同,应用程序通常更具动态性,复杂性,并且甚至由Bootstrap或ZURB Foundation提供的组件组成。 随着应用程序复杂性的增长,管理其CSS可能是一项艰巨的任务。
随着时间的流逝,已经开发了无数策略,例如OOCSS,ITCSS,BEM,Atomic CSS等,以保持CSS的组织性,可重用性和(关键地) 可扩展性 。 这些策略要求您和团队中的每个人都认真遵守这些约定。
但是,迟早,复杂性将再次蔓延,您将遇到如下样式规则:
html.progressive-image.js [data-progressive-image],html.progressive-image.js [data-progressive-image] * {
background-image: none !important;
mask-image: none !important;
opacity: 0
}
.main #section-enhanced-gallery-heroes.homepage-section.enhanced-gallery .with-single-item {
transform: translate(0, 0) !important
}
许多大型网站和应用程序上CSS的问题在于,要保持较低的特异性是如此困难,以至于在某种程度上,无法避免添加!important
。 而且在大型代码库上重构CSS十分棘手,因为删除样式可能会破坏其他组件。
在本教程中,我们将研究“ CSS模块”以及它如何帮助我们减少这些臭名昭著CSS问题。
注意 : 在Github上查看仓库以获取支持的代码示例。
使用CSS模块
简而言之,“ CSS模块 ”是一种将CSS类和ID重命名为唯一选择器的工具,从而可以将样式规则本地隔离到已分配的元素或组件。 假设我们有一个按钮,通常可以如下编写其样式规则:
.button {
background-color: #9b4dca;
border: 0.1rem solid #9b4dca;
border-radius: .4rem;
color: #fff;
cursor: pointer;
display: inline-block;
}
.button:focus,
.button:hover {
background-color: #606c76;
border-color: #606c76;
color: #fff;
outline: 0;
}
使用CSS模块,这些样式规则将重命名为:
._12We30_button {
background-color: #9b4dca;
border: 0.1rem solid #9b4dca;
border-radius: .4rem;
color: #fff;
cursor: pointer;
display: inline-block;
}
._12We30_button:focus,
._12We30_button:hover {
background-color: #606c76;
border-color: #606c76;
color: #fff;
outline: 0;
}
如果通过浏览器的DevTools浏览Facebook,Instagram或Airbnb等大型网站,您会发现CSS类和ID就是用这种模式命名的。 这是Airbnb主页上的示例:
多种成分
如果我们只有一个组件,那么使用CSS模块就没有多大意义,因此让我们将示例扩展到三个组件,并了解如何配置我们的项目以实现CSS模块。
创建一个组件
在第二个示例中,我们将构建三个组件。 具有三种不同样式的按钮。 我们将它们称为“主按钮”,“概述按钮”(也称为“鬼按钮”)和“清除按钮”。 我们将这些按钮放在单独的目录中。 每个目录将包含index.css
和index.js
。
在index.js
,我们创建元素并将类分配给该元素,如下所示:
// 1. Import the styles from index.css file.
import styles from './index.css';
/**
* 2. Creating a button element and add the class from index.css.
* @type {String}
*/
const button = `<button class="${styles['button']}">Save Changes</button>`;
// 3. Export the button to be used in the other files.
export default button;
使用ES6中的新import
指令,我们导入样式表,并将类和ID读取为JavaScript对象。 然后,我们创建一个元素,并使用本机JavaScript模板化添加名为.button
的类,该类也在ES6中引入。 最后,我们导出元素,以便也可以在其他JavaScript文件中导入和重用该元素。
在撰写本文时,并不是每个浏览器都实现了ES6规范中的最新JavaScript功能和语法。 因此,我们需要Babel将这些代码段转换为大多数浏览器都兼容JavaScript语法。
我们的样式表index.css
是纯CSS。 它包含许多用于设置按钮元素样式的选择器。
/* 1. Primary Button */
.button {
background-color: #9b4dca;
border: 0.1rem solid #9b4dca;
border-radius: .4rem;
color: #fff;
cursor: pointer;
display: inline-block;
font-size: 1.1rem;
font-weight: 700;
height: 3.8rem;
letter-spacing: .1rem;
line-height: 3.8rem;
padding: 0 3.0rem;
text-align: center;
text-decoration: none;
text-transform: uppercase;
white-space: nowrap;
}
/* More styles of the Primary Button here */
使用CSS模块的优点之一是,我们不必担心命名约定。 您仍然可以使用自己喜欢CSS方法(如BEM或OOCSS),但不强制执行任何操作。 您可以用最实用的方式为组件编写样式规则,因为类名最终将得到命名空间 。
在此示例中,我们将按钮组件的所有类命名为.button
而不是.button-primary
或.button-outline
。
在Shadow DOM中使用CSS时,即使不需要,我仍然有使用BEM表示法的习惯。 样式封装FTW!
— Razvan Caliman(@razvancaliman) 2017年7月31日
使用Webpack编译模块
当我们在HTML页面上加载index.js
时,浏览器中将不会显示任何内容。 在这种情况下,我们将必须编译代码以使其起作用。 我们将需要安装Babel , ES2015(ES6)的Babel预设和Webpack以及以下所谓的“加载程序”,以允许Webpack处理我们的源文件。
- babel-loader :使用Babel核心模块加载
.js
文件并转换源代码。 - css-loader :加载
.css
文件。 - style-loader :使用
<style>
将来自css-loader
内部样式注入HTML页面。
我们使用NPM安装这些软件包,并将其保存为package.json
文件作为我们的开发依赖项。
Webpack配置
与带有gruntfile.js
Grunt或带有gruntfile.js
Gulp gulpfile.js
,我们现在需要使用名为webpack.config.js
的文件来设置webpack.config.js
。 以下是我们项目中完整的Webpack配置:
var path = require('path');
module.exports = {
entry: './src/main.js', // 1.
output: {
path: path.resolve(__dirname, 'dist/js'),
filename: 'main.js'
},
module: {
loaders: [ // 4.
{
test: /\.css$/,
use: [
{
loader: 'style-loader'
}, {
loader: 'css-loader',
options: {
modules: true,
localIdentName: '[hash:base64:5]__[local]'
}
}
]
}, {
test: /\.js$/,
exclude: /node_modules/,
use: [
{
loader: 'babel-loader',
options: {
presets: ['es2015']
}
}
]
}
]
}
}
该配置告诉Webpack将我们的主要JavaScript文件main.js编译到/dist/js
目录。 我们还启用了css-loader
的modules
选项,并将类和ID命名模式设置为[hash:base64:5]__[local]
。 我们使用babel-loader
来编译我们的ES6 JavaScript文件。
一旦安装了依赖项并设置了配置,就将所有三个按钮都导入main.js文件中。
// Import the button elements;
import ButtonPrimary from './button-primary';
import ButtonOutline from './button-outline';
import ButtonClear from './button-clear';
// Add the element to the content;
document.getElementById('content').innerHTML = `${ButtonPrimary}${ButtonOutline}${ButtonClear}`;
然后运行webpack
命令,如下所示:
./node_modules/.bin/webpack
当在浏览器中加载/dist/js/main.js
时,我们应该看到按钮被添加了类,这些类的名称与我们在css-loader
设置的模式相同。 我们还可以找到通过styles元素添加到页面的styles
。
组成
诸如LESS和Sass之类CSS预处理器允许我们通过扩展其他样式表中的另一个类或ID来重用样式。 使用CSS模块,我们可以使用compose
指令执行相同的操作。 在此示例中,我将在三个按钮之间共享的通用样式规则放置在另一个文件中,并从新文件中导入了该类,如下所示:
.button {
/* Extend .button class to apply the basic button styles */
composes: button from "./button.css";
color: #fff;
background-color: #9b4dca;
border: 0.1rem solid #9b4dca;
}
一旦代码重新编译并加载到浏览器中,我们可以发现按钮具有两个类。 现在,页面中还注入了四个style
元素,其中包括_3f6Pb__button
类,其中包含我们组件的通用样式规则。
在Vue中使用CSS模块
在实际的项目中,我们可能不会使用纯JavaScriptCSS模块。 相反,我们将使用像Vue这样JavaScript框架。 幸运的是,CSS模块已通过vue-loader
集成到Vue; 一个Webpack加载器,它将编译.vue
。 以下是如何将主按钮移植到.vue
组件中的.vue
。
<template>
<button v-bind:class="$style.button"><slot></slot></button>
</template>
<script>
export default {
name: 'button-primary'
}
</script>
<style module>
.button {
/* Extend .button class to apply the basic button styles */
composes: button from "./button.css";
color: #fff;
background-color: #9b4dca;
border: 0.1rem solid #9b4dca;
}
.button[disabled] {
cursor: default;
opacity: .5;
}
.button[disabled]:focus,
.button[disabled]:hover {
background-color: #606c76;
border-color: #606c76;
}
.button:focus,
.button:hover {
background-color: #606c76;
border-color: #606c76;
color: #fff;
outline: 0;
}
</style>
在Vue中,我们将module
属性添加到style
元素,如上所示,以启用CSS模块。 当我们编译这段代码时,我们将得到几乎相同的结果。
结语
对于某些人来说,这将是全新的东西。 乍看之下,如果CSS模块的概念有点让人头疼,那是完全可以理解的。 因此,让我们回顾一下我们在CSS模块中学到的知识。
- “ CSS模块”允许我们通过重命名或
namespacing
类名称来封装样式规则,从而最大程度地减少随着代码库增长而对选择器特异性的冲突。 - 这也使我们可以更舒适地编写类名,而不必遵循一种特定的方法。
- 最后,由于样式规则已耦合到每个组件,因此当我们不再使用组件时,样式也会被删除。
在本文中,我们几乎没有涉及CSS模块和其他现代Web开发工具(如Babel,Webpack和Vue)的表面。 因此,在这里,我整理了一些参考资料以进一步研究这些工具。
进一步参考
- 在Github上查看演示
- 使用Babel开始对ES6进行编码 :Tuts +课程
- Vue入门 :Tuts +课程
- Instant Webpack 2 :Tuts +课程
- 降低CSS特异性的策略
- 丹尼尔·伊甸园:行动缓慢并解决问题
css模块化的解决方案