如果你在大型前端项目里用过 styled-components 或 Emotion,大概率遇到过这么个尴尬:写起来很爽的 CSS-in-JS,到了运行时却要承担一串序列化、注入 <style> 标签的额外开销——页面首屏变慢,而且样式越多越明显。更头疼的是,类型安全基本靠约定,一个拼错的 prop 名可能等到测试才发现。Bamboo 这个新项目换了个思路:把样式计算整个挪到构建期,运行时只留下一串编译好的类名。它本质上是一个零运行时的 CSS-in-JS 库,底层用 Rust 写的提取器(Oxc,也就是基于 Rust 的 JavaScript 解析器),配合 Vite 插件工作。
它的核心做法是:你写代码时还是正常的对象字面量语法,比如 `css({ fontSize: 'lg', fontWeight: 'bold' })`,编译器会在构建时把这些调用解析成全局共享的原子类(atomic class),最终打包产物里只剩 `<div className="fs_lg fw_bold">` 这样纯的类名组合。因为作用域内所有相同声明都会合并成一条 CSS 规则,所以即便项目里有 2 万个样式调用点,产出的 CSS 量也控制得非常好。对于更复杂的变体需求,Bamboo 提供了 `cva()`、`sva()` 这类 API(CVa 是 Class Variance Authority 的缩写,用来定义可组合的样式变体),编译后这些变体调用会被解析成一张有限决策表,运行时只根据条件选一个类名,没有任何动态样式计算的余地。
最让我觉得聪明的一点是它把“死引用”变成了编译错误。比如你引用了不存在的 token 或 pattern,构建会直接报错,给出类似 `ERR_BAMBOO_DEAD_IMPORT` 的信息,告诉你哪个文件引用了不存在的绑定。这样一来,删改样式时不用担心遗留垃圾代码。同时它会自动剪枝——把没有用到的 token、keyframes、reset 规则全部删掉。仓库里示例应用的数据是 styles.css 从 18KB 减到 3.9KB,节约了 36% 到 78% 的体积。另外它还支持按路由分割 CSS,懒加载的 chunk 只带上自己需要的原子类,这对首屏优化是实打实的帮助。开发模式下还有 source map,能直接定位某条规则来自哪个文件、哪个样式调用。
Bamboo 是从 Panda CSS fork 出来的,API 做了减法,保留了你可能已经熟悉的 `css`、`cva`、`sva`、`cx` 这些方法,所以迁移成本主要是改个名字。它的设计哲学是彻底去掉运行时回退,如果遇到运行期才出现的动态样式值,直接编译失败——这种“激进”反而保证了产物的确定性。目前它已经在 Contra 公司的生产环境跑了,那个 UI 有超过 2 万处 `css()` 调用,还有 3000 多个测试覆盖。
这套思路对哪些场景特别有用?首先是构建大型设计系统或组件库的团队,尤其是多个产品共享一套 token 和样式规范,构建期统一提取能避免运行时重复计算。其次是性能敏感型应用,比如移动端 H5、嵌入到原生壳的 WebView,减少运行时 JS 执行时间对帧率影响很大。第三就是希望严格类型安全的工程,Bamboo 的配置会自动生成 typed styled-system,编辑器就能自动补全 token 名和变体名,编译期就把拼写错误拦住了。
但也有几个需要注意的坑:它目前只支持 Vite 作为集成层,CLI 只负责生成代码,如果你用的是 Webpack 或 Rspack 就得自己想办法。另外,因为所有样式必须在构建期确定,所以那些依赖运行时动态计算的场景(比如根据窗口尺寸实时改变样式值)就不太合适,得靠 CSS 变量或别的机制来兜底。不过它提供了 `fallback(100dvh, 100vh)` 这类渐进增强写法,也支持 View Transitions API,算是把现代 CSS 的能力包装得比较顺手。
如果你想做一个零运行时 CSS-in-JS 的方案,Bamboo 是个很好的参考样本——它证明了用 Rust 做静态提取,配合 Vite 的模块图做全局原子化,是完全可行的。哪怕不用它,这种“把样式编译成可分析的静态数据”的理念也值得借鉴:让构建系统尽早发现问题,远比在浏览器里调样式要划算得多。
内容与图片版权归原作者所有 · 原文: https://bamboocss.com/docs/overview/getting-started/