如何编写类型安全的CSS模块

2023-05-09 16:20:15 浏览数 (1)

快来免费体验ChatGpt plus版本的,我们出的钱 体验地址:https://chat.waixingyun.cn

在这篇文章中,作者讨论了如何在 CSS 模块中使用类型安全。由于 CSS 模块在运行时生成类名并在构建之间更改,因此很难以类型安全的方式使用它们。一种解决方案是使用 TypeScript 定义文件为每个 CSS 模块手动创建类型,但更新这些文件非常繁琐。文章提出了一个问题,即假设在 CSS 模块中添加或删除了一个类名。

下面是正文~

使用TypeScript的好处之一是它显著减少了特定错误的发生,例如拼写错误;它甚至使访问原型方法和执行重构更加容易。在编译时捕获的错误可以提高正常运行时间,让客户更加满意,并减少开发人员的紧急呼叫压力。

使用TypeScript,很容易为我们的应用程序的业务逻辑和控制流程进行类型标注,但如果我们也能使CSS类安全,那该多好呢?确保正确的CSS类名已经就位可以确保所需的样式应用于给定的组件,从而防止由于排版错误而导致样式错位。

在本文中,我们将讨论CSS模块是什么,探讨它们的开发者体验缺陷,并学习如何通过使用TypeScript自动化来解决这些问题。让我们开始吧!

什么是CSS模块?

CSS模块提供了一种在现代Web应用程序中编写模块化和作用域CSS样式的方法。这些样式特定于你的应用程序的特定组件或模块。你可以使用常规CSS编写CSS模块。

在构建时,使用 Vite 或其他类似的工具,CSS 模块为 CSS 文件中定义的每个类生成唯一的类名。然后在 JavaScript 中使用生成的类名来引用 CSS,从而使 CSS 模块化和可重用,避免类名冲突或不必要的重复。

在撰写本文时,CSS类名不再是全局的,解决了许多像BEM这样的方法论旨在解决的问题,但无需手动努力。然而,在CSS模块中遵循BEM仍然取决于用例而有益。

将 CSS 模块添加到你的项目中

如果你想在下一个 TypeScript 应用程序中使用 CSS 模块,则有几个选项。

现代构建工具如 Vite 和 Snowpack 支持 CSS 模块化,但如果你使用的是 webpack,可能需要包含一些小的配置。

一旦构建设置完成,可以按照CSS模块约定添加带有 module.css 扩展名的CSS文件:

代码语言:javascript复制
// button.module.css
.green {
    background-color: 'green';
}

.blue {
    background-color: 'blue';
}

.red {
    background-color: 'red';
}

为了应用这些样式并利用上述好处,我们应该从 TypeScript 文件中导入 CSS 模块并绑定 HTML。请记住,下面的示例是用 React 编写的,但语法与其他 UI 库非常相似:

代码语言:javascript复制
// Component.tsx
import styles from './button.module.css'

const Component = () => (
    <>
        <button className={styles.green}>I am green!</button>
        <button className={styles.blue}>I am blue!</button>
        <button className={styles.red}>I am red!</button>
    </>
)

如果你在本地运行上面的代码,您会注意到返回的 styles 没有被严格限制类型。相反,它们被视为任何类型。此外,TypeScript 编译器不会在类名不存在时通知你。

开发者体验的改进

CSS模块是一个很好的工具,但由于类名是在运行时生成的并且在构建之间发生更改,因此很难以类型安全的方式使用它们。

你可以使用TypeScript定义文件手动为每个CSS模块创建类型,但更新它们很繁琐。假设从CSS模块中添加或删除了一个类名。在这种情况下,必须手动更新类型,否则类型安全性将无法按预期工作。

对于上面的例子,输入应该如下:

代码语言:javascript复制
declare const styles: {
  readonly green: string;
  readonly blue: string;
  readonly red: string;
};
export default styles;

这些类型在我们修改相关的CSS模块之前都能很好地工作。一旦我们修改了它,我们就必须更新类型。如果我们忘记手动更新类型,可能会出现一些讨厌的UI错误。

我们忘记修改相关的类型文件:

代码语言:javascript复制
// button.module.css.d.ts
declare const styles: {
  readonly green: string;
  readonly blue: string;
  readonly red: string; 
 we forgot to update the types! 

};
export default styles;


// Component.tsx
import styles from './button.module.css'

const Component = () => (
    <>
        <button className={styles.green}>I am green!</button>
        <button className={styles.blue}>I am blue!</button>
        <button className={styles.red}>I am red!</button>
    </>
)

在这个例子中展示的情况可能看起来不相关,但随着代码库和贡献者数量的增长,这种重复和容易出错的过程将会阻碍对类型系统的信任。引用不存在或打错字的 CSS 类将无法按预期样式化 HTML,这可能很快演变成开发人员失去对工具的信任。让我们学习如何自动化它!

自动化

在这种情况下,自动化解决方案很简单。我们将自动生成类型,而不是手动创建,并提供一个脚本来验证生成的类型是否最新,以避免不正确的 CSS 模块类型泄漏到编译步骤中。

有多种方法可以实现这一点。例如,我们可以构建一个将 CSS 转换为 TypeScript 定义的提取器。但是,为了避免重复造轮子,我们将利用开源包 typed-css-modules。

使用 npm i typed-css-modules 在你的项目中安装包,然后将类型生成添加到你的主开发脚本中的 package.json 脚本中:

代码语言:javascript复制
"watch": "vite & tcm --watch .",

添加检查最新类型的功能。如果生成的类型在 package.json 脚本中不正确,则会失败:

代码语言:javascript复制
"check:up-to-date-types": "tcm --listDifferent .",

有了这两个脚本,现在可以自动保持 CSS 模块类型定义的同步,并检查类型是否保持最新。

根据项目的不同,你可能更喜欢在本地或服务器上运行这些脚本,可能作为你的 CI 流水线的一部分。为了完善示例,我们将描述如何使用 husky 将它们作为 Git Hook 运行。

使用 npx husky-init && npm install 安装并设置 Git Hook runner。要设置在每次提交之前运行 CSS 模块类型检查的 pre-commit Hook,请将 .husky/pre-commit 文件修改为以下内容:

代码语言:javascript复制
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

npm run check:up-to-date-types

在每次提交之前,钩子将运行并验证类型是否最新。

总结

在TypeScript生态系统中工作具有巨大的潜力,但是,当过度依赖手动流程时,很容易破坏类型系统的信任或产生不必要的摩擦。

CSS模块非常棒,通过一些额外的配置,很容易为生成的类添加类型安全性。您应该自动化繁琐的工作,以便你的团队可以专注于构建出色的产品。

代码部署后可能存在的BUG没法实时知道,事后为了解决这些BUG,花了大量的时间进行log 调试,这边顺便给大家推荐一个好用的BUG监控工具 Fundebug

0 人点赞