大家好,很高兴又见面了,我是"高级前端进阶",由我带着大家一起关注前端前沿、深入前端底层技术,大家一起进步,也欢迎大家关注、点赞、收藏、转发,您的支持是我不断创作的动力。
CSS-in-JS 技术用于在同一文件中编写组件和样式,同时保持简单性和清晰度。
根据 styled-components 创建者 Max Stoiber 的说法,超过 60% 的 React 安装者还安装了 CSS-in-JS 库。 在 JavaScript 中编写 CSS 非常流行,尤其是在使用 React 或 Angular 等 JS 框架时。 但是许多库可用于简化编写 CSS-in-JS 的过程,本文将向大家介绍6款零运行时 CSS-in-JS 库。
CSS-in-JS 是一种样式技术,使用 JavaScript 直接对组件进行样式设置,其使用变量来定义组件的 CSS。 该变量将包含所有 CSS 属性,确保组件与其指定的样式无缝衔接。
随着基于组件的样式变得越来越流行,CSS-in-JS 近年来也有所增加。 由于大多数现代 JavaScript 框架都是组件化,从而进一步推动 CSS-in-JS 的流行。 CSS 现已嫣然成为 JavaScript 的一个模块,可以在需要时自由定义和使用。
在类似 React 的现代框架中,开发者可以通过编写 CSS-in-JS 使用内联技术在 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 具有以下优点:
使用 CSS-in-JS 有一些缺点,包括:
编译时是指用高级、人类可读语言编写的代码被编译器转换为低级、机器可读代码,而运行时是指程序转换后的代码在最终用户的计算机上运行。
改善 CSS-in-JS 由于双重解析而导致的性能时间损失的解决方案之一是,库可以首先将 CSS-in-JS 块转换为单独的 CSS 文件。 然后,浏览器将读取这些样式并将其应用到网页,最终节省生成 style 标签时浪费的时间。 这称为零运行时 CSS-in-JS。 它对于性能至关重要的规模化或复杂的项目特别有用。
Linaria 的编译时 CSS-in-JS 工具提供了介于纯 CSS 解决方案和运行时 CSS-in-JS 方法之间的中间解决方案,即编译时方案。
Linaria 具有以下特征:
总之,Linaria 在编译时生成 CSS 类并将其提取到单独的 CSS 文件中,而不是在运行时生成。 这使得可以避免运行时 CSS-in-JS 解决方案的性能损失,同时保留组件隔离和动态样式匹配的优势。
import { css } from '@linaria/core';import { modularScale, hiDPI } from 'polished';import fonts from './fonts';// Write your styles in `css` tagconst 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>;
使用 Linaria 的缺点主要包括以下两点:
下面是 Linaria 的完整的 Webpack 示例配置:
const webpack = require('webpack');const path = require('path');const MiniCssExtractPlugin = require('mini-css-extract-plugin');const dev = process.env.NODE_ENV !== 'production';module.exports = { mode: dev ? 'development' : 'production', devtool: 'source-map', entry: { app: './src/index', }, output: { path: path.resolve(__dirname, 'dist'), publicPath: '/dist/', filename: '[name].bundle.js', }, optimization: { noEmitOnErrors: true, }, plugins: [ new webpack.DefinePlugin({ 'process.env': { NODE_ENV: JSON.stringify(process.env.NODE_ENV) }, }), new MiniCssExtractPlugin({ filename: 'styles.css' }), // 将所有文件中的 CSS 提取到单个 styles.css 中 ], module: { rules: [ { test: /\.js$/, exclude: /node_modules/, use: [ { loader: 'babel-loader' }, // 最终通过Babel处理 { loader: '@linaria/webpack-loader', options: { sourceMap: dev }, }, // 添加js文件的linaria的自定义loader ], }, { test: /\.css$/, use: [ { loader: MiniCssExtractPlugin.loader, }, { loader: 'css-loader', options: { sourceMap: dev }, }, ], }, { test: /\.(jpg|png|gif|woff|woff2|eot|ttf|svg)$/, use: [{ loader: 'file-loader' }], }, ], }, devServer: { contentBase: [path.join(__dirname, 'public')], historyApiFallback: true, },};
Astroturf 允许开发者在 JavaScript 文件中编写 CSS,而无需添加任何运行时层,并使用现有的 CSS 处理管道。
Astroturf 具有以下特征:
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>;}
Astroturf 的缺点主要包括:
Reshadow 用 JavaScript 编写。 该库提供了许多功能,最值得注意的是为 React 等虚拟 DOM 框架提供了 Shadow DOM 开发人员体验,同时还支持 CSS-in-JS 语法。
Reshadow 的典型特征包括:
import styled, { css } from 'reshadow';const styles = css` button { font-size: 16px; cursor: pointer; padding: 10px 15px; border-radius: 20px; border: 2px solid; background-color: white; color: darkorange; }`;const Button = ({ children, ...props }) => styled(styles)(<button {...props}>{children}</button>);
Reshadow 的缺点包括:
vanilla-extract 是 TypeScript 中的零运行时样式表。使用本地范围(locally scoped)的类名和 CSS 变量在 TypeScript(或 JavaScript)中编写样式,然后在构建时生成静态 CSS 文件。
基本上,它是“TypeScript 中的 CSS Modules”,但具有作用域 CSS 变量 等更多特性。
// styles.css.tsimport { createTheme, style } from '@vanilla-extract/css';export const [themeClass, vars] = createTheme({ color: { brand: 'blue', }, font: { body: 'arial', },});export const exampleStyle = style({ backgroundColor: vars.color.brand, fontFamily: vars.font.body, color: 'white', padding: 10,});
vanilla-extract 的缺点包括:
Treat 是可主题化、静态提取的 CSS-in-JS,运行时间接近于零。使用 Treat 时,样式在 .treat.js 或 .treat.ts 文件中声明。 Treat 执行 .treat.js 文件并在构建时生成所有 CSS 规则,仅打包生成的 CSS 样式。
如果使用主题功能,Treat 会在构建时生成所有 CSS 样式。 然后,当切换主题时,它会在运行时交换预先生成的类。
Treat 的特征可以总结为以下几点:
import { style } from 'treat';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>;};
Treat 的缺点包括:
Goober 是一个流行的、轻量级的、零依赖包。 虽然它不是严格意义上的零运行时 CSS-in-JS 解决方案,但其内置的 extractCss 函数允许开发者提取静态 CSS 文件并将它们注入到 <head> 标记中,就像在零运行时 CSS-in- 中一样。
使用 Goober 的优点包括:
该 API 的灵感来自于 emotion 的 styled 函数。意思是,您使用 tagName 调用它,它会返回该标签的 vDOM 组件。请注意,需要在使用样式函数之前运行安装程序。
import { h } from 'preact';import { styled, setup } from 'goober';// Should be called here, and just oncesetup(h);const Icon = styled('span')` display: flex; flex: 1; color: red;`;const Button = styled('button')` background: dodgerblue; color: white; border: ${Math.random()}px solid white; &:focus, &:hover { padding: 1em; } .otherClass { margin: 0; } ${Icon} { color: black; }`;
Goober 的缺点包括:
https://github.com/callstack/linaria
https://blog.logrocket.com/comparing-top-zero-runtime-css-js-libraries/
https://github.com/astroturfcss/astroturf
https://github.com/yandex/reshadow
https://github.com/vanilla-extract-css/vanilla-extract
https://github.com/seek-oss/treat
https://github.com/cristianbote/goober
https://medium.com/@arnabroyy/21-best-javascript-and-css-library-in-2023-for-web-development-e6e6af939a1e