发布于 2026-01-06 0 阅读
0

一种不同的 CSS-in-JS 方法……包括 TypeStyle 基础知识、原始 CSS、SSR 等等!大家有什么想法?

一种不同于 CSS-in-JS 的方法……

这个想法

字体样式

基础知识

原始 CSS

社会主义共和国

还有更多!

有什么想法?

这篇文章摘自我的博客,请务必访问我的博客查看更多最新内容。

如果你是一名 Web 开发人员,并且已经使用过一些 JS 框架(尤其是 React),那么你可能对CSS-in-JS的概念并不陌生。简单来说,它就是通过 JavaScript 而不是传统的 CSS 来创建 CSS 样式表。它比 SCSS 之类的解决方案略胜一筹,因为它能让你持续访问所有 J​​S 的优势。此外,它还能简化 CSS 样式的管理,并提升整体开发体验(DX)。

现在,让我们回顾一下TypeScript——一个包含静态类型系统的 JavaScript 超集。它通过额外的工具、建议和类型安全进一步提升了用户体验。那么问题来了——如果我们把 CSS-in-JS 和 TypeScript 结合起来会发生什么?答案是——TypeStyle诞生了!请耐心听我讲解,我们将一起探索这种组合能带来哪些好处,它是否值得你投入时间和精力,以及如何使用它!

这个想法

首先,让我们退一步,探讨一下为什么会有人将 TypeScript 与 CSS-in-JS 的概念结合起来。答案很简单——为什么不呢?! CSS-in-JS 本身就是一个通用的概念,它显然与 CSS 和 JS 相关,而 TypeScript 只是一个 JS 的超集,可以轻松访问其所有底层特性。所以,这样做完全没有意义。

更进一步来说,这种混合方式的潜在优势让它更加引人入胜!CSS-in-JS 的概念及其实现库,都旨在让 CSS 更“易于维护”。正如你可能知道的,它们实现这一目标的方式各不相同。有的允许你以对象的形式定义 CSS 类,有的允许你以模板字面量的形式定义,还有一些则通过提供 Babel 插件使整个过程变得更加复杂。别误会,所有这些方法都有各自的优点,当然,这取决于你的具体使用场景。但是,它们也存在一些缺点……

几乎所有这些库都缺乏类型安全。当然,我指的是 TypeScript。它们大多是用纯 JavaScript 编写的,只有一些不完整的外部类型定义。这种情况可能是因为创建合适的静态类型 API 非常困难,尤其是在用 JavaScript 表示 CSS 时。CSS 的属性和特殊规则(例如 `style` @media)实在太多,难以实现。不过,我们仍然可以尝试!

字体样式

那么,TypeStyle 是什么呢?想必你已经知道了——它是一个用 TypeScript 编写的 CSS-in-JS 库。它的主要目标是让CSS 更易于维护类型安全。除此之外,它还内置了一些非常实用的功能。

TypeStyle 与许多 CSS-in-JS 库的不同之处在于它是运行时库。它使用所有 CSS 相关 API(我在之前的文章中讨论过),直接用 JavaScript 创建所有样式表,而无需进行任何预处理。因此,TypeStyle 具有极佳的“可移植性”。由于其基于运行时的模型和较小的体积(压缩后最小约 6 KB),您可以直接替换它,即可立即使用!

该库也与框架无关。正因如此,TypeStyle 比其他一些库更注重模仿 CSS 设计。当然,这也给某些用户带来了一些“缺点”,例如——最显著的是——不支持自动添加前缀和其他 CSS 后处理功能。

当然,TypeStyle 最大的亮点在于其类型定义。它的 API 在实现基于 TS 的自动补全和代码提示功能方面表现出色。或许 CSS 永远无法做到 100% 类型安全,但这个库确实将我们现有的功能提升到了一个全新的高度。

基础知识

那么,在做了一些介绍和说明之后,让我们直接进入 TypeStyle API 的简要概述。请记住,它并不是一个大型库,而且它的文档已经尽力解释了所有内容。话虽如此,如果您想了解更多信息,请去查阅文档。

npm install typestyle
Enter fullscreen mode Exit fullscreen mode

CSS 类

TypeStyle 最基本的用途是创建简单的CSS 类

import { style } from "typestyle";

const className = style({
    backgroundColor: "red",
    width: 100,
    height: 100
});
Enter fullscreen mode Exit fullscreen mode

通过使用style()该函数,我们创建了一个新的 CSS 类,之后可以通过返回的哈希类名来访问它。提供的配置对象可以像其他任何配置对象一样使用,包括解构赋值Object.assign()和其他一些很棒的功能。只需向该函数提供任意数量的配置对象,即可实现类似的功能style()

import { style, types } from "typestyle";

const rect: types.NestedCSSProperties = {
    width: 100,
    height: 100
};

const className = style({
    backgroundColor: "red",
    ...rect
}); // or style({backgroundColor: "red"}, rect);
Enter fullscreen mode Exit fullscreen mode

使用此类模式会导致样式配置的所有“组件”失去类型安全性和 TypeStyle 支持。如果您正在使用 TypeStyle 并且不希望发生这种情况,您可以像上面的示例一样,借助 TypeStyle 提供的类型,直接为对象指定类型。

嵌套

许多其他 CSS-in-JS 库都提供了对类似函数的基本 TS 支持style()。TypeStyle 的独特之处在于其高度集成。TypeStyle 处理伪类的方式就是一个很好的例子。请看:

// ...
const className = style({
    backgroundColor: "red",
    ...rect,
    $nest: {
        "&:hover": {
            backgroundColor: "green"
        }
    }
});
Enter fullscreen mode Exit fullscreen mode

该库需要特殊的嵌套属性$nest,以便为不同的伪类等提供样式配置。这使得 TypeScript 能够推断正确的类型,从而为常用的伪类提供尽可能多的支持。该$nest属性也可以用于普通的嵌套选择器。但是,请注意,这种用法会导致 TypeScript 不支持嵌套选择器,并且在大多数 CSS-in-JS 场景中,带有嵌套选择器的类难以管理。

助手

一般来说,style()TypeStyle 的核心功能就在于此。它既简单又直观。库的其余部分基本上都是基于此功能构建的,并提供了额外的辅助函数和其他实用工具。

媒体查询

此类辅助函数最值得注意的例子包括media()用于类型安全媒体查询的函数。

import { style, media } from "typestyle";
// ...
const className = style(
    rect,
    media({minWidth:0,maxWidth:600}, {backgroundColor: "red"}),
    media({minWidth:601}, {backgroundColor: "green"}),
);
Enter fullscreen mode Exit fullscreen mode

media()函数是一个mixin,输出一个标准的配置样式。你可以把它看作是$nestproperty 的一个不错的替代品。

// ...
const className = style(
    rect,
    $nest: {
        "@media only screen and (max-width: 600px)": {
            backgroundColor: "red"
        },
        // ...
    }
);
Enter fullscreen mode Exit fullscreen mode

不错吧?$nest某些高级用例可能仍然需要这个属性。记住,因为我们使用的是 JS/TS,所以你可以随时创建自己的 mixin,以便为你的主样式配置赋予一些结构和外观

动画

就像媒体查询一样,CSS 关键帧动画也是一个同样“特殊”的功能,在 CSS-in-JS 中使用起来可能比较困难。为此,TypeStyle 再次提供了一个不错的辅助函数keyframes()

import { style, keyframes } from "typestyle";
// ...
const animationName = keyframes({
  '0%': { color: 'red' },
  '100%': { color: 'green' }
})

const className = style({
    ...rect,
    animationName: animationName,
    animationDuration: '2s',
});
Enter fullscreen mode Exit fullscreen mode

该函数会返回新创建的动画的哈希名称,供您后续使用。正是这种直观性让我非常喜欢这个库。

级联

最后,如果您使用 React 或类似的简单className属性,您可能会喜欢这个classes()辅助函数。它会将所有提供的类名连接起来并返回结果。

import { classes } from "typestyle";
// ...
const classStr = classes(className, className2);
Enter fullscreen mode Exit fullscreen mode

原始 CSS

正如上面的例子所示,TypeStyle 提供了一组简洁但功能有限的辅助函数。毕竟,一个 6 KB 的库能容纳多少东西呢?总之,重点在于该库并非为所有情况都提供了辅助函数。如果你愿意,可以使用 mixin、组件对象等工具轻松地自行创建所需的辅助函数。

你可能已经猜到,TypeStyle 将其所有类和其他功能都应用到一个单独的样式表(单个<style/>标签)中,而该样式表是借助一些 CSS 相关的 Web API 创建的。在使用 TypeStyle 的原始 CSS函数时cssRule(),记住这一点非常重要cssRaw()

import { cssRule, cssRaw } from "typestyle";
// ...
cssRule(".red-rect", {
  ...rect
  backgroundColor: "red"
});

cssRaw(`
.green-rect {
  height: 100px;
  width: 100px;
  background-color: green;
}
`);
Enter fullscreen mode Exit fullscreen mode

我认为这些函数不需要深入解释。第一个函数允许你使用自定义字符串选择器创建 CSS 规则,这在一定程度上是类型安全的。cssRaw()而另一个函数则应该仅用于加载 CSS 库,即便如此,你或许还不如使用普通的外部 CSS 文件。它完全不提供任何类型安全保障!

当然,这类函数非常有用——尤其是在您希望所有 CSS 都以 CSS-in-JS 的方式编写时。这类函数可以与例如 ` @import<style>` 规则一起使用,其中规则的位置至关重要。因此,理解 `TypeStyle` 只能作用于单个样式表非常重要。对于此类用例,您应该cssRaw()在任何其他 CSS 相关调用之前使用它,以便将自定义规则放置在样式表的顶部

社会主义共和国

我之前提到过 TypeStyle 是运行时专用的。这意味着它默认情况下完全不依赖任何 Babel 插件之类的东西。如果你认为从性能角度来看这不是最佳选择,那就再想想。性能损失几乎察觉不到(至少对我来说是这样),而且你真的不应该为了性能而牺牲可维护性。但是,如果你不想改变主意,还有另一种方法。

TypeStyle 内置了对服务器端渲染(SSR) 和静态页面生成的支持。由于它使用单个样式表,TypeStyle 提供了一个易于使用的函数getStyles()来提取其所有规则。

import { style, getStyles } from "typestyle";
// ...
const className = style({
  backgroundColor: "red"
  ...rect,
});

getStyles();
/* Example result:
hashed-class-name {
    height: 100px;
    width: 100px;
    background-color: red
}
*/
Enter fullscreen mode Exit fullscreen mode

使用此getStyles()函数,您可以轻松使用 TypeStyle 的所有功能,包括带哈希的 CSS 类名,而不会造成任何(哪怕是最轻微的)性能损失。只需将此调用的结果放入<style/>模板文件的 `<head>` 标签中,即可!当然,如果您知道如何操作,您甚至可以非常轻松地(很可能)为此创建自己的 Babel 插件。

还有更多!

由于我不想让这篇文章变成一份文档,而是一篇简单易懂、适合初学者的教程,所以我们就到此为止吧。官方文档中还有一些有趣的功能和需要注意的地方。如果你对这个库感兴趣,我强烈建议你阅读文档——它们写得非常出色!不过,即便如此,凭借你在本文中了解到的功能,你应该能够轻松地以类型安全、易于维护且富有表现力的方式表示大部分 CSS 代码。

有什么想法?

那么,你觉得TypeStyle 怎么样?你喜欢它所代表的这种略有不同的 CSS-in-JS 方法吗?请在下方评论区留言告诉我。如果你喜欢这篇文章,也欢迎点赞评论提出对未来文章的建议。想要获取更多最新内容,请关注我的 TwitterFacebook 主页个人博客。希望你喜欢这篇文章,祝你今天过得愉快

文章来源:https://dev.to/areknawo/a- Different-approach-to-css-in-js-58ol