Goober
极致轻量的跨框架 CSS-in-JS 方案
Goober 以极小包体积提供类似 styled-components 和 Emotion 的开发体验,适合追求性能和跨框架兼容性的项目。
Goober
🐿️ 不到 1KB 的极致轻量 CSS-in-JS 方案,为追求极致包体积的项目而生
技术简介说明
Goober 是一个体积极小(gzip 后仅约 1.25KB)的 CSS-in-JS 样式库,由罗马尼亚工程师 Cristian Bote 创建并维护。它提供了与 Emotion、styled-components 相似的 API(styled()、css()、keyframes()、createGlobalStyles() 等),但包体积仅为其 1/6 到 1/10——这让它成为对性能敏感的 Web 应用、嵌入式组件、微前端和 Web Components 场景中的理想选择。
与大多数 CSS-in-JS 库强绑定 React 生态不同,Goober 采用框架无关的设计哲学。它通过 setup(pragm) 将任意 vDOM 渲染函数(React 的 createElement、Preact 的 h、Fre 的 h,甚至原生 DOM 操作)接入核心,真正实现了"一套 API,多框架通用"。这种极小的体积和极高的兼容性,让 Goober 在 Web Components、微前端、跨框架组件库等场景下有着不可替代的优势。
2025–2026 年,Goober 持续保持活跃迭代(最新 v2.1.19,2026 年 5 月发布),新增了 CSP nonce 支持、迁移至 Vite 构建、持续优化 TypeScript 类型导出等。在 CSS-in-JS 整体向"零运行时"演进的大背景下,Goober 凭借极致的轻量运行时和零配置体验,依然在特定场景下保持独特竞争力。
基本信息
| 项目 | 说明 |
|---|---|
| 名称 | Goober |
| 最新版本 | v2.1.19(2026 年 5 月 13 日发布) |
| 官网 | goober.js.org |
| 文档站 | goober.rocks |
| GitHub | github.com/cristianbote/goober |
| npm | npmjs.com/package/goober |
| License | MIT |
| Stars | ~3.3k ⭐ |
| 维护者 | Cristian Bote |
| 框架依赖 | 无(框架无关,通过 setup() 接入) |
| TypeScript | 内置类型定义(goober.d.ts) |
| 包体积 | gzip 后约 1.25KB(同类最小) |
快速上手
安装
# npm
npm install goober
# yarn
yarn add goober
# pnpm
pnpm add goober基础配置(React)
import React from 'react'
import { styled, setup } from 'goober'
// 必须调用 setup,传入框架的 createElement 函数
setup(React.createElement)
// 使用模板字符串语法定义组件
const Button = styled('button')`
background: palevioletred;
color: white;
padding: 8px 16px;
border: none;
border-radius: 4px;
cursor: pointer;
&:hover {
background: darkorchid;
}
`
export default function App() {
return <Button>Click me</Button>
}基础配置(Preact)
import { h } from 'preact'
import { setup, styled } from 'goober'
// Preact 使用 h 函数作为 pragma
setup(h)
const Title = styled('h1')`
color: #333;
font-size: 24px;
`
export default function App() {
return <Title>Hello Goober!</Title>
}原生 JS(Web Components)
import { styled, setup, css } from 'goober'
// 原生 DOM 场景,传入自定义渲染函数
setup((tag, props, ...children) => {
const el = document.createElement(tag)
Object.entries(props || {}).forEach(([k, v]) => {
if (k === 'className') el.setAttribute('class', v)
else if (k.startsWith('on')) el.addEventListener(k.slice(2).toLowerCase(), v)
else el.setAttribute(k, v)
})
children.flat().forEach(child => el.appendChild(
child instanceof Node ? child : document.createTextNode(child)
))
return el
})
// 定义样式
const btnClass = css`
background: #007bff;
color: white;
padding: 8px 16px;
border: none;
border-radius: 4px;
`
// 应用样式
const btn = document.createElement('button')
btn.className = btnClass
btn.textContent = 'Click me'
document.body.appendChild(btn)核心概念与架构
架构设计
Goober 采用极简的运行时架构,核心只有几个关键模块:
goober
├── core # 核心:样式注入引擎、类名哈希生成
├── styled # styled() API —— 创建 styled 组件
├── css # css() API —— 生成类名字符串
├── glob # glob/glob`...` —— 注入全局样式
├── extractCss # SSR 支持 —— 提取并清空已生成的 CSS
├── prefixer # goober/prefixer —— 自动添加浏览器前缀
└── should-forward-prop # 控制 DOM 属性转发
工作流程
1. setup(pragm) 接入框架渲染函数
2. styled('div')`...` 定义组件时:
├── 解析模板字符串 / 样式对象
├── 生成哈希类名(如 go1234567)
└── 返回包装组件(挂载 className)
3. 渲染时:
├── 组件渲染为 vDOM 节点(含 className)
└── 样式注入到 <style> 标签(幂等去重)
4. SSR 时:
└── extractCss() 提取所有已注入样式为 <style> 字符串
样式生成原理
Goober 将模板字符串或样式对象解析后,通过一个轻量哈希函数生成唯一类名(如 go1a2b3c),然后将 CSS 规则注入到 DOM 中的 <style> 标签。相同的样式内容只会注入一次,避免重复。
核心特性
1. 极致轻量(<1KB gzip)
Goober gzip 后仅约 1.25KB,是同类 CSS-in-JS 库中最小的:
| 库 | gzip 大小 |
|---|---|
| Goober | ~1.25 KB |
| Emotion | ~7.4 KB |
| styled-components | ~12.6 KB |
2. 框架无关
通过 setup(pragm) 接入任意 vDOM 框架,官方支持 React、Preact、Fre 和原生 JS。
3. 完整的 API 体系
styled()—— 创建 styled 组件css()—— 生成类名字符串keyframes()—— 定义动画createGlobalStyles()—— 全局样式组件glob—— 全局样式(函数式)extractCss()—— SSR 样式提取
4. SSR(服务端渲染)支持
通过 extractCss() 在服务端提取所有已生成的 CSS,插入到 HTML 的 <head> 中,实现关键 CSS 注入,避免 FOUC(Flash of Unstyled Content)。
5. TypeScript 完整支持
内置 goober.d.ts 类型定义文件,支持泛型 props、组件类型推断、样式对象类型检查。
6. 主题支持(Theming)
import { styled, setup, ThemeProvider } from 'goober'
const theme = {
colors: {
primary: '#007bff',
secondary: '#6c757d',
},
spacing: {
sm: '8px',
md: '16px',
},
}
const Button = styled('button')(
(props) => `
background: ${props.theme.colors.primary};
padding: ${props.theme.spacing.sm};
color: white;
border: none;
border-radius: 4px;
`
)7. 嵌套规则与媒体查询
const Card = styled('div')`
padding: 16px;
background: white;
&:hover {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
h2 {
font-size: 20px;
margin-bottom: 8px;
}
@media (max-width: 768px) {
padding: 8px;
}
`8. 动态样式(Props 驱动)
const Box = styled('div')`
background: ${(props) => props.primary ? '#007bff' : '#6c757d'};
padding: ${(props) => props.large ? '24px' : '12px'};
color: white;
border-radius: 4px;
`
// 使用
<Box primary large>Primary & Large</Box>
<Box>Secondary & Default</Box>9. 自动前缀(Autoprefixer)
通过 goober/prefixer 插件自动为 CSS 属性添加浏览器前缀:
import { styled, setup } from 'goober'
import { prefix } from 'goober/prefixer'
setup(React.createElement, prefix)
const Box = styled('div')`
display: flex;
user-select: none;
-webkit-tap-highlight-color: transparent;
`10. CSP(内容安全策略)支持
自 v2.1.17(2025 年 10 月)起支持 CSP nonce:
// 在 setup 时传入 nonce
setup(React.createElement, undefined, () => ({
nonce: 'your-csp-nonce-value'
}))⚠️ CSP nonce 支持是 v2.1.17 新增特性,早期版本需要自行处理。参考 Issue #471。
生态图
核心集成
| 包名 | 说明 |
|---|---|
goober | 核心库(<1KB) |
goober/prefixer | 自动浏览器前缀插件 |
goober/should-forward-prop | 控制 DOM 属性转发 |
框架集成
| 集成方式 | 说明 |
|---|---|
| React | setup(React.createElement) |
| Preact | setup(h) |
| Fre | setup(h) |
| 原生 JS / Web Components | 自定义 setup 函数 |
构建工具集成
| 工具 | 包名 / 说明 |
|---|---|
| Babel | babel-plugin-transform-goober —— 支持 styled.div 语法糖 |
| Babel Macro | goober/macro —— 零配置 Babel macro |
| CSS Prop | @agley/babel-plugin-goober-css-prop —— 支持 css prop |
| Next.js | 官方示例:stackblitz.com/edit/github-mlwqmi |
| Gatsby | gatsby-plugin-goober —— 自动 pragma 配置 + SSR |
| Preact CLI | preact-cli-goober-ssr —— 预渲染页面关键 CSS 提取 |
| Vite | 官方文档站使用 Vite 构建,开箱支持 |
社区插件
| 插件 | 说明 |
|---|---|
goober-global | 全局样式工具 |
goober-autoprefixer | 社区 autoprefixer 实现 |
@nksaraf/goober | 社区 fork 版本 |
适用场景
1. Web Components / 自定义元素
Web Components 对包体积极为敏感,Goober 的 <1KB 特性使其成为 shadow DOM 内样式的理想选择。
2. 微前端子应用
微前端架构中,每个子应用都需要独立的样式方案。Goober 的轻量特性让每个子应用的样式开销降到最低。
3. 嵌入式组件 / Widget
嵌入第三方网站的 Widget(客服插件、支付按钮、评论组件等)需要极小的包体积以避免影响宿主页面性能,Goober 是绝佳选择。
4. 跨框架组件库
当你需要构建同时兼容 React、Preact 等多框架的组件库时,Goober 的框架无关设计让样式层无需为每个框架重复实现。
5. 性能敏感型应用
对于 Lighthouse 分数至关重要的高性能网站,Goober 的 <1KB 体积和极快的运行时(~169,157 ops/sec SSR)可以显著减少首屏加载时间。
6. 原型开发与小型项目
无需复杂配置,npm install goober 后 setup() 一下即可使用,适合快速原型验证和小型单页应用。
7. CSP 严格环境
需要遵守严格内容安全策略的企业应用,v2.1.17+ 的 CSP nonce 支持使 Goober 可以合规使用。
开发与工程化
开发模式
Goober 本身不提供 HMR(热模块替换)支持,但由于样式通过类名注入,搭配 Vite/Webpack 的 HMR 可以正常工作。修改样式代码后,新类名会自动生成并注入。
生产构建
Goober 的样式在运行时生成,无法像 Linaria/vanilla-extract 那样在构建时提取为静态 CSS 文件。这意味着:
- ✅ 支持动态样式(props 驱动)
- ❌ 不支持零运行时(runtime-free)构建
- ⚠️ 样式计算开销在运行时发生
Babel 优化
使用 babel-plugin-transform-goober 可以获得更优雅的语法:
npm install -D babel-plugin-transform-goober// .babelrc
{
"plugins": ["babel-plugin-transform-goober"]
}// 转换前
const Title = styled('h1')`...`
// 转换后(更优雅的语法)
const Title = styled.h1`...`TypeScript 配置
Goober 内置类型定义,无需额外安装 @types/goober。对于 styled 组件的 props 类型:
import { styled } from 'goober'
interface ButtonProps {
primary?: boolean
size?: 'sm' | 'md' | 'lg'
}
const Button = styled('button')<ButtonProps>`
background: ${(props) => props.primary ? '#007bff' : '#6c757d'};
padding: ${(props) => {
switch (props.size) {
case 'sm': return '4px 8px'
case 'lg': return '12px 24px'
default: return '8px 16px'
}
}};
color: white;
border: none;
border-radius: 4px;
`样式对象(替代模板字符串)
使用 JSON 对象语法可以进一步减小包体积:
const Button = styled('button')({
background: '#007bff',
color: 'white',
padding: '8px 16px',
border: 'none',
borderRadius: '4px',
'&:hover': {
background: '#0056b3',
},
'@media (max-width: 768px)': {
padding: '4px 8px',
},
})💡 使用样式对象代替模板字符串,可以进一步减小约 200B 的包体积。
性能与安全
性能数据
| 指标 | Goober | styled-components | Emotion |
|---|---|---|---|
| gzip 体积 | ~1.25 KB | ~12.6 KB | ~7.4 KB |
| SSR 吞吐量 | ~169,157 ops/sec | ~10,000 ops/sec | ~73,000 ops/sec |
| 运行时开销 | 极低 | 中等 | 较低 |
数据来源:LogRocket Blog 基准测试
性能优势
- 极小体积:1.25KB gzip,解析和执行速度极快
- 高效哈希:轻量哈希算法生成唯一类名,去重注入
- 无额外依赖:核心库无第三方依赖,不增加 node_modules 解析负担
- SSR 友好:
extractCss()一次性提取所有样式,避免额外 I/O
安全注意事项
- CSP 支持:v2.1.17+ 支持 nonce 属性,可在严格 CSP 环境下安全使用(参考 Issue #471)
- XSS 防护:模板字符串插值会经过 Goober 内部的样式解析,不会被直接注入为 HTML
- DOM 注入:样式通过
<style>标签注入到<head>,不直接操作用户内容 - SSR 并发:单例 store 在高并发 SSR 场景下可能存在竞态条件(参考 Issue #158),建议在请求级别隔离样式缓存
技术对比
综合对比表
| 特性 | Goober | styled-components | Emotion | Linaria | vanilla-extract | Tailwind CSS |
|---|---|---|---|---|---|---|
| 体积(gzip) | ~1.25 KB | ~12.6 KB | ~7.4 KB | ~0 KB(零运行时) | ~0 KB(零运行时) | ~0 KB(原子化) |
| 运行时 | ✅ 轻量 | ✅ 较重 | ✅ 中等 | ❌ 编译时提取 | ❌ 编译时提取 | ❌ 编译时提取 |
| 框架绑定 | ❌ 无关 | React | React/Preact | React | 框架无关 | 框架无关 |
| TypeScript | ✅ 内置 | ✅ 内置 | ✅ 内置 | ✅ 内置 | ✅ 内置 | ✅ 内置 |
| SSR | ✅ extractCss | ✅ 内置 | ✅ 内置 | ✅ 自动 | ✅ 自动 | ✅ 自动 |
| CSP nonce | ✅ v2.1.17+ | ✅ 内置 | ✅ 内置 | N/A | N/A | N/A |
| 动态样式 | ✅ | ✅ | ✅ | ❌ | ❌ | ⚠️ 有限 |
| 主题系统 | ✅ | ✅ | ✅ | ❌ | ✅ | ❌ |
| 嵌套语法 | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ |
| 媒体查询 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Autoprefixer | ✅ 插件 | ❌ | ❌ | N/A | N/A | N/A |
| 零运行时 | ❌ | ❌ | ❌ | ✅ | ✅ | ✅ |
| 学习曲线 | 极低 | 低 | 低 | 中 | 中 | 中 |
| 社区规模 | 中等(3.3k⭐) | 大(40k+⭐) | 大(17k+⭐) | 中(9k+⭐) | 中(5k+⭐) | 极大(85k+⭐) |
选型建议
- 极致包体积优先 → Goober
- 零运行时 + 类型安全 → Linaria / vanilla-extract
- 原子化 CSS + 设计系统 → Tailwind CSS / Panda CSS
- React 生态成熟方案 → Emotion / styled-components
- 跨框架组件库 → Goober / vanilla-extract
最佳实践
1. 始终调用 setup()
这是最常见的遗漏。Goober 必须通过 setup() 知道如何创建 vDOM 元素,否则会报错:
// ✅ 正确:在应用入口调用一次
import { setup } from 'goober'
setup(React.createElement) // 或 h(Preact)
// ❌ 错误:不调用 setup 直接使用 styled
import { styled } from 'goober'
const Box = styled('div')`...` // 运行时报错2. 优先使用样式对象
样式对象比模板字符串更小、更快,且更容易被 TypeScript 类型检查:
// ✅ 推荐:样式对象
const Box = styled('div')({
padding: 16,
color: 'white',
})
// ⚠️ 可用:模板字符串(适合需要动态插值的场景)
const Box = styled('div')`
padding: 16px;
color: white;
`3. 合理使用 shouldForwardProp
防止自定义 props 被透传到 DOM 元素(避免 React 警告):
import { styled, setup } from 'goober'
import { shouldForwardProp } from 'goober/should-forward-prop'
setup(React.createElement, undefined, undefined, shouldForwardProp)
const Box = styled('div')<{ $primary?: boolean }>`
background: ${(props) => props.$primary ? '#007bff' : '#6c757d'};
color: white;
padding: 8px 16px;
`💡 使用
$前缀命名自定义 props 是 styled-components 社区的惯例,Goober 的shouldForwardProp默认过滤$开头的 props。
4. SSR 场景正确使用 extractCss()
// server.js(Node.js)
import { extractCss } from 'goober'
import { renderToString } from 'react-dom/server'
import App from './App'
const html = renderToString(<App />)
const css = extractCss() // 提取并清空样式缓存
const fullHtml = `
<!DOCTYPE html>
<html>
<head>
<style>${css}</style>
</head>
<body>
<div id="root">${html}</div>
</body>
</html>
`5. 全局样式用 createGlobalStyles()
不要在组件内使用 glob 注入全局样式(每次渲染都会执行),应该使用 createGlobalStyles():
import { createGlobalStyles } from 'goober'
const GlobalStyles = createGlobalStyles`
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
line-height: 1.5;
}
`
// 在应用根部渲染一次
function App() {
return (
<>
<GlobalStyles />
<Main />
</>
)
}6. 动画复用 keyframes()
import { keyframes, styled } from 'goober'
const spin = keyframes`
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
`
// 多个组件共享同一个动画定义
const Spinner = styled('div')`
animation: ${spin} 1s linear infinite;
width: 24px;
height: 24px;
border: 2px solid #ccc;
border-top-color: #007bff;
border-radius: 50%;
`技术局限与边界
1. 无零运行时支持
Goober 是运行时方案,所有样式在浏览器(或 Node.js SSR)中动态计算并注入。无法像 Linaria、vanilla-extract、Panda CSS 那样在构建时提取为静态 CSS 文件。这意味着:
- 增加运行时 CPU 开销(虽然极小)
- 首屏需要等待 JS 执行后才能注入样式
- 与 React Server Components(RSC)的兼容性存在挑战
2. SSR 并发竞态风险
Goober 使用单例 store 存储所有已生成的样式。在高并发 SSR 环境下,多个请求共享同一 store 可能导致样式串扰或提前清空(参考 Issue #158)。需要自行在请求级别做缓存隔离。
3. 社区规模有限
相比 styled-components(40k+ ⭐)和 Emotion(17k+ ⭐),Goober 的 3.3k ⭐ 社区规模较小,意味着:
- 第三方插件和工具较少
- 遇到问题的社区解答资源有限
- 企业级采用案例相对较少
4. 无组件交换(Component Swapping)
Goober 不支持 as prop 等组件交换语法(如 <Button as="a" href="/">),这在某些设计系统中是常见需求。
5. 无自动标签(Labels)
Goober 不生成人类可读的类名(如 Button-primary-abc123),只生成哈希类名(如 go1a2b3c),调试时需要依赖 source map 或浏览器 devtools 反查。
6. 生态绑定度低
不像 Emotion 有 @emotion/react 的 css prop 和 JSX pragma 的深度集成,Goober 的 css prop 支持依赖第三方 Babel 插件,配置相对繁琐。
7. RSC(React Server Components)兼容性
作为运行时方案,Goober 在 React Server Components 环境中无法正常工作(服务端无法执行 setup() 和样式注入)。这是所有运行时 CSS-in-JS 方案的共同挑战,但在 2026 年 RSC 日益普及的背景下,这一局限变得更加突出。
学习资源
官方资源
教程与文章
- 📖 Goober: A lightweight CSS-in-JS solution — LogRocket Blog — 详尽的入门教程与性能对比
- 📖 Replacing Styled Components with a 1KB alternative Goober — Dev.to — 迁移实战经验
- 📖 New CSS-in-JS lightweight packages for React and Preact — 轻量 CSS-in-JS 横评
视频
- 🎥 Wrangle Your CSS in JS for Peanuts using Goober ft. Cristian Bote — YouTube — 作者亲自讲解
- 🎥 What's goober all about? A live session about css-in-js — YouTube — 深入探讨
在线示例
- 💻 Next.js with Goober — StackBlitz
- 💻 Using Goober (CSS-in-React) — CodePen
- 💻 Preact + Goober — CodeSandbox
2026 年现状
版本迭代
截至 2026 年 7 月,Goober 最新版本为 v2.1.19(2026 年 5 月 13 日发布)。近期重要更新:
| 版本 | 时间 | 变更 |
|---|---|---|
| v2.1.19 | 2026-05-13 | 迁移至 Vite 构建、精简包体积 |
| v2.1.18 | 2025-10-03 | 修复 CSP nonce 属性注入时序 |
| v2.1.17 | 2025-10-02 | 新增 CSP(内容安全策略)支持 |
| v2.1.16 | 2025-10-11 | 修复 :has 伪类多选择器 bug |
| v2.1.15 | 2025-10-07 | TypeScript 类型:JSX → React.JSX |
CSS-in-JS 行业趋势
2025–2026 年,CSS-in-JS 领域正经历显著范式转变:
- 零运行时成为主流 — Linaria、vanilla-extract、Panda CSS、StyleX 等编译时方案快速增长
- 原子化 CSS 持续扩张 — Tailwind CSS 周下载量超 1200 万,稳居生态第一
- 传统运行时方案承压 — styled-components(进入维护模式)和 Emotion 在新项目中的采用率下降
- RSC 加速范式迁移 — React Server Components 的普及使得运行时样式注入方案面临根本性挑战
Goober 的定位
在这个趋势下,Goober 的独特价值在于:
- 极致轻量无可替代 — 在 <1KB 体积下提供完整 CSS-in-JS 能力,没有任何竞品能在同一量级匹敌
- 框架无关的稀缺性 — 在 Emotion/styled-components 强绑定 React 的市场中,Goober 的跨框架能力是其护城河
- Web Components / 微前端的刚需 — 这些场景对包体积极度敏感,Goober 是最佳选择之一
但也面临挑战:
- 零运行时方案(vanilla-extract、Panda CSS)在类型安全和构建时优化方面优势明显
- 原生 CSS 能力(
@scope、:has()、容器查询)的增强减少了部分 CSS-in-JS 的需求 - RSC 生态的成熟对运行时方案构成结构性威胁
结论
Goober 在 2026 年依然是一个健康、活跃的项目,在极致包体积和框架无关这两个维度上保持着独特且不可替代的地位。对于 Web Components、嵌入式组件、微前端、跨框架组件库等场景,Goober 仍然是最推荐的 CSS-in-JS 方案。但对于以 React 为主、追求零运行时和类型安全的大型应用,vanilla-extract、Panda CSS 或原生 CSS 方案可能是更面向未来的选择。
参考资料
- goober.js.org - 官方文档
- goober.rocks - 官方文档站
- GitHub - cristianbote/goober
- npm - goober
- Goober: A lightweight CSS-in-JS solution — LogRocket Blog
- Wrangle Your CSS in JS for Peanuts using Goober — YouTube
- The State of CSS-in-JS in 2026 — PkgPulse
- Replacing Styled Components with a 1KB alternative Goober — Dev.to
- CSP Nonce Issue #471 — GitHub
- SSR extractCss Issue #158 — GitHub
- Goober vs Linaria 对比 Issue #533 — GitHub
- gatsby-plugin-goober — Gatsby