• 5个流行的 CSS-in-JS 零运行时库对比总结
  • 发布于 2个月前
  • 119 热度
    0 评论
对于 React 开发同学来说,CSS-in-JS 技术并不陌生,使用它,我们可以在同一个文件中编写组件及其样式,并保持简单和整洁。根据 styled-components 的创建者 Max Stoiber (@mxstbr)的说法,超过 60% 的开发者安装 React 同时也会安装一个 CSS-in-JS 库。本文将探讨一些流行的 CSS-in-JS 零运行时库,并给出优缺点对比,主要内容如下:

一.CSS-in-JS 介绍
CSS-in-JS 是一种通过使用 JavaScript 直接为组件设置样式的技术。我们使用变量来定义组件的 CSS。变量将包含所有的 CSS属性,以确保组件与其指定的样式无缝结合。随着组件化样式的流行,CSS-in-JS 近年来越来越多地被使用。现代 JavaScript 框架大多是基于组件的,这提升了 CSS-in-JS的使用率。CSS 现在已经成为 JavaScript 的一个模块,可以在需要的时候定义和使用。

在像 React 这样的现代框架中,您可以使用内联技术在JavaScript 文件的 JSX 部分中编写 CSS。但是这种技术可能会令人困惑,可读性较差,并且会破坏代码流程。在通过库编写 CSS-in-JS 时,它无法取代 CSS 的模块化。

在 CSS-in-JS 中,我们在变量中定义样式,然后通过使用变量标签对组件进行包装,就能使用这些样式。styled 标签是从库中导入的。它创建了一个带有预定义样式的 React 组件,后面跟着您想要使用的 HTML 标签。在下面的示例中,我们使用 h1 标签,它将根据定义的 CSS 属性进行自定义。在编写完这些内容之后,我们定义属性如下:
const Title = styled.h1`
  font-family: sans-serif;
  font-size: 48px;
  color: #f15f79;
`;
接下来,我们将内容包装在变量标签中:
const App = () => <Title>Hello world!</Title>;
这就是您在大多数 CSS-in-JS 库中定义样式的方式。

二.CSS-in-JS 优缺点对比
1) 优点:
轻松共享代码:使用 CSS-in-JS 更容易和更有效地共享代码,因为其他人可以轻松理解您的代码,而无需在项目中查找组件和样式。
减少 DOM 负载:由于我们在同一个文件中定义了 CSS 和组件,当组件加载时,该组件的 CSS 只会加载一次,从而减少了对虚拟 DOM 的不必要负载。
在 CSS 中使用 JavaScript:因为 CSS 在 JavaScript 文件中定义,我们可以使用复杂的 JavaScript 逻辑来定义 CSS 属性的值。
更好地 CSS 错误处理:由于 CSS 也经过编译过程,因此在编译过程中我们会收到错误信息,这样就可以轻松发现并解决 CSS 中的错误。

可移植性:将样式和组件放在同一个文件中,方便在其他项目中使用该组件。


2) 缺点:
因为样式定义在 JavaScript 文件中,如果禁用JavaScript,它会影响组件的样式。
样式会被解析两次,一次是由库解析,一次是在插入样式时由浏览器解析。

如传统方式加载网页时,浏览器只读取 CSS 并应用。而使用 CSS-in-JS 时,浏览器会动态生成 CSS 样式标签,然后读取和应用它们到网页上。读取和生成这些样式会耗费性能时间。


三.CSS-in-JS 编译与运行时间
用于开发应用程序的高级编程语言通常是为人类阅读和理解而创建的。为了使机器能够理解用高级语言(如JavaScript或Python)编写的代码,代码需要由编译器或解释器将其转换为低级、机器可读的代码。编译时间是指将用高级、人类可读的语言编写的代码通过编译器转换为低级、机器可读的代码的时间。

运行时间是指程序转换后的代码在最终用户的机器上运行的时间。为了改善因双重解析而损失的性能时间,常见的解决方案是程序库可以先将 CSS-in-JS 块转换为单独的 CSS 文件。然后,浏览器会读取并将这些样式应用到网页中,最终节省了通常在生成样式标签时浪费的运行时间。这就是所谓的零运行时 CSS-in-JS。对于性能要求较高的大型或复杂项目,这种方法尤其有用。

要实现零运行时 CSS-in-JS ,有各种流行库可以使用,不需要我们开发者花时间去开发。下面我们将介绍一些常用的库。

四.CSS-in-JS 流行的零运行时库
4.1.Linaria
https://github.com/callstack/linaria
Linaria 使用 TypeScript 编写,是最受欢迎的零运行时库,在 GitHub 上拥有 10.9k 个 star。它的语法简单易用,几乎兼容所有现代框架。Linaria 在创建用于生产的构建时,会将 CSS-in-JS 转换为单独的 .css 文件。

Linaria 提供了许多功能,包括:
Dynamic prop-based style:你可以使用 prop 来定义 CSS 属性的值
CSS source map:在大型项目中,很难找到为组件定义样式的位置。如果在编写 CSS 时将 CSS source map 设置为 true,它将在开发工具中显示生成 CSS 的类名来源。
Linting CSS:StyleLint 可以帮助你避免 CSS 错误,并在样式中执行约定俗成的规则
JavaScript for logic:使用 Linaria,你可以在编写 CSS 时使用 JavaScript 逻辑
1)安装 Linaria:
npm i @linaria/core @linaria/react @linaria/babel-preset
2)项目中使用:
import { css } from "@linaria/core";
import { modularScale, hiDPI } from "polished";
import fonts from "./fonts";

// Write your styles in `css` tag
const header = css`
  text-transform: uppercase;
  font-family: ${fonts.heading};
  font-size: ${modularScale(2)};
  ${hiDPI(1.5)} {
    font-size: ${modularScale(2.5)};
  }
`;

// Then use it as a class name
<h1 className={header}>Hello world</h1>;
3)Linaria 缺点:
Difficult to implement:需要设置 Babel,使用上有成本
Setting up the bundler:从 JS 文件中提取 CSS 需要使用捆绑程序,如 Rollup 或 webpack,使用上有成本
4.2.Vanilla-extract
https://github.com/vanilla-extract-css/vanilla-extract
vanilla-extract 是另一款流行的零运行时 CSS-in-JS 库,在 GitHub 上拥有 8.8k 个 star。它与框架无关,这意味着你可以在 vanilla JavaScript 或任何前端框架中使用它。

有了 vanilla-extract,你就可以在 JavaScript 或 Typescript 中编写本地范围的样式和变量,并在构建时生成 CSS 文件。它还提供了一个可选的应用程序接口,用于动态主题化应用程序。

主要特性如下:
支持捆绑器:它集成了大多数流行的前端 React 框架和工具,如 Webpack、Vite、Parcel 和 Babel。
完善的文档
易于设置
同时支持 JavaScript 和 TypeScript

类型安全:提供了 fully-typed API,以改善开发者体验


1)安装:
npm install @vanilla-extract/css
2)项目中使用:
使用 vanilla-extract 时,您需要在 .css.ts 或 .css.js 文件中声明样式:
import { style, globalStyle } from "@vanilla-extract/css";

export const buttonStyles = style({
  backgroundColor: "#1e4db6",
  color: "#fff",
  padding: "10px 20px",
  borderRadius: "5px",
  border: "none",
});

globalStyle("body", { backgroundColor: "#eee", margin: 0 });
从 .css.ts 或 .css.js 文件中导入已声明的样式,并使用 class 属性(如果使用的是 React,则使用 className)进行应用,如下图所示:
import { useState } from "react";
import { buttonStyles } from "./app.css";

function App() {
  const [count, setCount] = useState(0);
  return (
    <button className={`${buttonStyles}`} onClick={() => setCount(count + 1)}>
      Count: {count}
    </button>
  );
}

export default App;
3)vanilla-extract 的缺点:
维护不太积极:有大量未解决的 issue 和 PR,其中一些 issue 和 PR 的时间已超过一年。这表明 vanilla-extract 可能未得到积极维护。

4.3.Goober
https://github.com/cristianbote/goober
Goober 是一款流行的轻量级零依赖包,在 Github 上有 3k 个 star。严格来说,它并不是零运行时间 CSS-in-JS 解决方案,但其内置的 extractCss 函数允许你提取静态 CSS 文件,并将它们注入 <head> 标签,就像零运行时间 CSS-in-JS 包在服务器端渲染时那样。

使用 Goober 的优势:
包大小:Goober 超级轻量(约 1kb)
框架支持:Goober 与框架无关
1)安装:
npm i goober
2)项目中使用:
const express = require("express");

const { styled, extractCss, setup } = require("goober");
const preact = require("preact");
const render = require("preact-render-to-string");

setup(preact.h);
const app = express();

const Button = styled("button")`
  background-color: #1e4db6;
  color: #fff;
  padding: 10px 20px;
  border: none;
  border-radius: 10px;
`;

const App = () => preact.h(Button, null, "Click me!");

app.get("/", (_, res) => {
  const app = render(preact.h(App));
  const style = extractCss();

  const html = `
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>SSR</title>
        <style id="_goober">
          ${style}
        </style>
    </head>
    <body>
     ${app}     
    </body>
    </html>
`;
  res.send(html);
});

app.listen(process.env.PORT || 3000);
3)Goober 的缺点:
技术上并非零运行时间 CSS-in-JS
设置:它需要 Babel 或 webpack,使用上有些成本
支持其他捆绑程序:Goober 没有与某些捆绑程序进行开箱即用的集成
4.4.Astroturf
https://github.com/astroturfcss/astroturf
Astroturf 是替代 Linaria 的一个很好的选择。在 GitHub 上拥有超过 2.2k 个 star,Astroturf 通过保持 CSS 完全静态且没有运行时解析来帮助您实现零运行时。通过 Astroturf 的作用域样式表、React 和 props 和 组件变体,有多种编写CSS-in-JS的方式。

Astroturf 的主要特点:
灵活性:某些框架只适用于特定的 CSS 处理方式,但 Astroturf 具有灵活性,并且与大多数框架兼容。
Props 支持:Astroturf 支持 React 的 props 功能,可以根据 props 来为组件设置样式。
能够使用现有工具:您可以使用 Sass 和 PostCSS 在 JavaScript 中编写样式定义。
1)安装:
npm i astroturf
2)项目中使用:
import React from "react";
import { css } from "astroturf";

const btn = css`
  color: black;
  border: 1px solid black;
  background-color: white;
`;

export default function Button({ children }) {
  return <button className={btn}>{children}</button>;
}
3)Astroturf 的缺点:
文档:Astroturf 在 GitHub 上缺乏适当的说明文件。它还缺乏贡献指南,并且文档很简短,经常省略重要细节。
实现:提取样式时,Astroturf 需要像 Rollup 或 Webpack 这样的捆绑器,实现上比较困难。
4.5.Treat
https://github.com/seek-oss/treat
Treat 是另一款流行的零运行时 CSS-in-JS 包,在 GitHub 上拥有超过 1.2k 个 star。在使用 Treat 时,样式会在 .treat.js 或 .treat.ts 文件中声明。在构建时,Treat 会执行 .treat.js 文件并生成所有 CSS 规则,只捆绑生成的 CSS 样式。

如果使用主题功能,Treat 会在构建时生成所有 CSS 样式。然后,当切换主题时,它会在运行时交换预生成的类。虽然 Treat 提供对 React 和 TypeScript 的原生支持,但其核心 API 可以集成到其他框架中,从而使其与框架无关。

使用 Treat 的好处包括:
良好的文档
可主题化:Treat 具有内置主题功能
类型安全:Treat 内置类型安全功能
1)安装:
npm i treat
2)项目中使用:
在 .teat.js 或者 .treat.ts 中声明样式。
import { style } from 'treat';
// 堆代码 duidaima.com
export const buttonStyle = style({
  backgroundColor: "#1e4db6",
  color: "#fff",
  padding: "10px 20px",
  borderRadius: "5px",
  border: "none",
});
导入样式声明:
import React from 'react';
import { buttonStyle } from './Button.treat';

export const Button = () => {
  return <button className={buttonStyle}>Click me</button>
}
3)Treat 的缺点:
Webpack 设置:Treat 需要 webpack,使用上有成本

对前端框架的开箱即用支持比较有限


总结
零运行时的 CSS-in-JS 库提供了许多好处,尤其是当开发者参与到多个或者大型项目时。CSS-in-JS 的未来还是不错的,特别是在Web 开发和现代框架方面。本文分析了几个比较流行的 CSS-in-JS 零运行时库,大家可以在技术选型阶段根据自身需求、团队状况、人员能力来评估,按需使用。
用户评论