在前端开发中,有两种类型的人:喜欢创建布局的人和使用香草CSS的人。只是在开玩笑。香草CSS可以具有很大的优势,例如完全控制您的样式并了解每个元素的目的。但是,老实说:即使规格在每个版本中都在不断改进,使用CSS有时可能会有些乏味,尤其是在开始时,当我们不完全确定每个道具是什么原因,当然,可能会发生意外的结果。
今天,我在这里与您谈论我对CSS做出的最好的决定之一,现在我将其纳入了所有项目(除非我选择使用像Tailwind这样的库)。我说的是 CSS模块。
/* style.module.css */
.saveButton {
color: green;
}
// form.jsx
import styles from "./styles.module.css"
export const Form = () => {
// ...
return (
<form onSubmit={handleOnSubmit}>
<input name="email" />
<button className={styles.saveButton} />
</form>
)
}
classNames之间的碰撞
如果您使用了BEM或某些类命名约定来改善CSS体系结构,则您可能注意到随着代码库的增长,这些班级名称变得更容易碰撞 。有时,您已经安装的组件库甚至可能发生。
这是混乱的。此外,即使组件的名称及其属性可以帮助您进行类及其各种衍生品的名称及其各种衍生产品的名称。
。那么,CSS模块如何帮助我们进行此操作?
CSS模块或如何隔离样式
使用CSS模块完成了三件事:
- 避免班级名称之间的冲突
- 轻松快捷地重构
- 避免使用诸如全球范围之类的反模式
让我们更详细地深入研究这些:
避免碰撞
从定义上讲,CSS模块中的样式被示例,这意味着它们仅影响应用它们的元素。因此,即使有多种样式指的是您项目中相同的元素,它也只能应用于已明确引用的元素。
例如:
/* style.module.css */
.title {
font-weight: bolder;
}
// header.jsx
import styles from "./styles.module.css"
export const Header = () => {
// ...
return (
<div>
<h1 className={styles.title}>Hello world!</h1> {/* ✅ Style applied */}
<h2 className="title">This is my first post</h2> {/* ❌ Style NOT applied */}
</div>
)
}
所有这些的重要性都在于该代码的处理方式以及这些类名称在浏览器中运行时如何出现这些代码。让我们看一个示例,示例js做什么,尽管结果可能取决于您使用的框架或设置的配置:
输入:
/* ProgressBar.module.css */
.background, .bar {
height: 4px;
border-radius: 999px;
}
.background {
background-color: var(--color-secondary);
width: 100%;
}
.bar {
background-color: var(--color-primary);
}
// ProgressBar.tsx
import styles from "./ProgressBar.module.css";
interface Props {
percentage: number;
}
export const ProgressBar = ({ percentage }: Props) => {
return (
<div className={styles.background}>
<div className={styles.bar} style={{ width: `${percentage}%` }} />
</div>
);
};
输出:
<div class="ProgressBar_background__v_qdp">
<div class="ProgressBar_bar__YFKLn" style="width: 50%;"></div>
</div>
您可以想象,CSS模块以一种或另一种方式实现样式范围的操作是在类中添加哈希。在这种情况下,为了使读取和调试应用程序更容易,模块名称出现在哈希之前,然后是类名称:module_class__hash
。
,但如前所述,这是Next.js的示例;它将与其他框架或配置不同。
容易重构
由于我们将CSS文件视为模块和类名为这些模块导出的变量,重命名类或识别未使用的类别 比以往任何时候都容易。
但是,请注意! ``即使可能不会发生班级碰撞,也必须命名课程,但仍应遵循一项惯例。尽管BEM可能不再提供太多的价值,但对于您的团队来说,建立适合您的命名约定很重要。 CSS模块无法解决命名问题。
例如,如果我们想将Button
组件更改为ActionButton
怎么办?让我们首先看一个 bem 的示例:
/* (BEFORE) Button.css */
.Button_label {
text-transform: uppercase;
}
/* (AFTER) ActionButton.css */
.ActionButton_label {
text-transform: uppercase;
}
// (BEFORE) Button.tsx
import "./Button.css";
export const Button = ({ label }) => {
return (
<button>
<span className="Button_label">{label}</span>
</button>
);
};
// (AFTER) ActionButton.tsx
import "./ActionButton.css";
import Icon from "icons"
export const ActionButton = ({ label }) => {
return (
<button>
<Icon />
<span className="ActionButton_label">{label}</span>
</button>
);
};
您可以看到,更改组件名称并不简单,因为您必须更改所有类名称(我们很幸运,在这种情况下只有一个)。即使现代IDE具有功能强大的发现和重新安置工具,但当不将类名称耦合到组件名称时,它总是更容易的。
现在,让我们使用CSS模块:
来看一下示例
/* (BEFORE) Button.module.css */
.label {
text-transform: uppercase;
}
/* (AFTER) ActionButton.module.css */
.label {
text-transform: uppercase;
}
// (BEFORE) Button.tsx
import styles from "./Button.module.css";
export const Button = ({ label }) => {
return (
<button>
<span className={style.label}>{label}</span>
</button>
);
};
// (AFTER) ActionButton.tsx
import styles from "./ActionButton.module.css";
import Icon from "icons"
export const ActionButton = ({ label }) => {
return (
<button>
<Icon />
<span className={style.label}>{label}</span>
</button>
);
};
您可以看到,通过不引用组件名称,它们更具脱钩,并且更容易进行更改 。
避免反诉讼
尽管CSS并不能阻止CSS架构差,但它确实有助于遵循一些最佳实践,例如组件化您的项目或避免全球样式。
让我们谈谈组件化,这种做法在网络框架和库(如Angular,React或Vue)的兴起中变得极为流行。这对于在应用程序中实现可重复使用性至关重要(尽管就像所有内容一样,您需要确定重构的限制)。
css模块激励我们为每个组件都有一个样式文件,这些样式总是与组件命名的变化齐头并进。换句话说,该组件是否从称为Button
到ActionButton
都没关系。样式在范围时被脱钩。
开始使用CSS模块
与香草CSS不同,CSS模块可能需要在使用前进行一些准备。这取决于您的捆绑器,预处理器以及您是否在项目中使用Typescript:
- webpack :这是需要更多文档的案例,但幸运的是,您会发现几个锅炉架,例如this one。
-
Next.js :时尚的网络框架具有CSS模块的本机support。您只需要开始创建
.module.css
文件。 - vite :CSS模块也与vite一起是本地的compatible,因此您不必做任何特别的事情才能开始使用它们。