CSS-in-JS 真香?别急,先看看你的团队能不能扛住
去年双11大促前两周,我们组被产品经理突然塞进来一个“轻量级活动页重构”需求。说是轻量,结果光样式就改了八轮——颜色、间距、动画节奏全在变。我正手写着 SCSS,隔壁新来的实习生小张一脸兴奋地跑来:“哥,要不咱们上 CSS-in-JS 吧?Emotion 写起来贼爽,还能自动 scope,不怕样式冲突!”
我看了眼 GitHub 上我们项目里那堆嵌套十层的 .container > .inner > .card 选择器,再瞅了瞅已经快秃的头顶,叹了口气:“小伙子,你先去把去年区块链浏览器那个页面的样式冲突修了再说。”
干了快两年,我一直是那种“能手写绝不靠工具”的老派前端。不是反对新技术,而是吃过太多“过度工程化”的亏。比如去年搞的那个基于 React + Webpack 的内部管理系统,为了追求“现代化”,硬是上了 Styled Components + ThemeProvider + GlobalStyle 三件套。结果上线三天后,测试同学反馈:移动端 Safari 样式错乱,白屏率飙升。
查了半天,发现是动态生成的 class 名称太长,加上 CSP(Content Security Policy)限制,某些旧设备直接解析失败。当时真的想砸电脑。
但我也不是顽固分子。毕竟天天泡在 GitHub 上看开源项目源码,像 Vercel、Shopify 这些大厂确实在用 CSS-in-JS 跑生产环境。问题不在技术本身,而在是否匹配团队现状和项目生命周期。
样式方案的本质:可维护性 vs. 开发体验
很多人讨论 CSS-in-JS 和传统 CSS,总爱扯“性能”、“bundle size”。但说实话,在大多数业务场景下,这两者的差异对用户体验的影响微乎其微。真正决定成败的,是代码可读性、协作成本和长期维护性。
举个例子:上周五晚上加班,运维突然 call 我,说某个页面在 IE11 下完全无法渲染。我打开 DevTools 一看,满屏红色报错:
CSS parse error: Invalid character '}' in expression
原来是某个同事用了 Emotion 的 css prop 写法,里面嵌套了模板字符串,而 IE11 对某些转义字符处理异常。这种问题在纯 CSS 里几乎不可能出现——因为语法简单到连 IE 都不好意思报错。
但反过来,传统 CSS 也有它的原罪:全局污染。记得刚入职那会儿,团队还在用原始 CSS,两个组件都叫 .button,结果一个加了 border-radius: 20px,另一个是直角,合并后直接炸了。后来引入 BEM 命名规范,虽然解决了冲突,但写起来像在背八股文:
.product-card__image--thumbnail {
width: 64px;
height: 64px;
border: 1px solid #eee;
}
看得人眼花缭乱,还容易拼错。这时候 CSS Modules 或 CSS-in-JS 的局部作用域优势就体现出来了。
实战对比:三种主流方案落地效果
为了这次重构,我拉着小张一起做了个微型实验:用三种方式实现同一个商品卡片组件。
方案一:传统 CSS + BEM(保守派的选择)
/* product-card.css */
.product-card {
padding: 16px;
border: 1px solid #ddd;
border-radius: 8px;
}
.product-card__image {
width: 100%;
height: auto;
}
.product-card__title {
font-size: 16px;
margin-top: 8px;
}
// ProductCard.jsx
import './product-card.css';
export default () => (
<div className="product-card">
<img className="product-card__image" src="..." />
<h3 className="product-card__title">商品标题</h3>
</div>
);
优点:
- 极致可读,新人一眼看懂
- DevTools 直接显示真实类名,调试方便
- 兼容性无敌,IE9 都能跑
缺点:
- 命名冗长,容易手误
- 无法动态传参(比如根据 props 改颜色)
- 需要额外配置 PostCSS、Autoprefixer 等
方案二:CSS Modules(折中路线)
/* ProductCard.module.css */
.container {
padding: 16px;
border: 1px solid #ddd;
border-radius: 8px;
}
.image {
width: 100%;
height: auto;
}
import styles from './ProductCard.module.css';
export default ({ isFeatured }) => (
<div className={`${styles.container} ${isFeatured ? styles.featured : ''}`}>
<img className={styles.image} src="..." />
</div>
);
优点:
- 自动哈希类名,避免冲突
- 保留 CSS 语法,学习成本低
- 支持组合(composes),复用性强
缺点:
- 动态样式仍需内联 style 或 JS 判断
- 类名在 DevTools 里是
_3XkLm2这种鬼东西,调试略烦
方案三:Emotion(激进派入场)
/** @jsxImportSource @emotion/react */
import { css } from '@emotion/react';
const container = (isFeatured) => css`
padding: 16px;
border: 1px solid ${isFeatured ? '#ff6b6b' : '#ddd'};
border-radius: 8px;
`;
export default ({ isFeatured }) => (
<div css={container(isFeatured)}>
<img css={{ width: '100%', height: 'auto' }} src="..." />
</div>
);
优点:
- 完全组件化,样式与逻辑紧耦合
- 支持 props 动态计算,不用写一堆条件类名
- 自动 vendor prefix,省心
缺点:
- 运行时注入
<style>标签,可能触发 CSP 问题 - SSR 需额外配置(extractCritical)
- 源码可读性下降,非前端成员看不懂
我把这三种方案的代码贴到团队群里,结果吵翻了天。后端大哥说:“你们前端能不能统一一下?我看不懂这些花里胡哨的。” 测试妹子吐槽:“上次那个 Emotion 页面,我在 Cypress 里定位元素好难,class 名一直在变。”
别被潮流带偏:选型要考虑“人”和“事”
我在 GitHub 上扒过不少明星项目。Vercel 的 Next.js 官网确实用 Emotion,但人家有专职前端基建团队,还能定制 ESLint 规则、自动化测试覆盖。反观我们这种 5 人小前端组,还得兼顾后端接口、部署、甚至偶尔客串 UI 设计师。
更别说我们还有几个对接政府系统的项目——必须支持 IE11,CSP 策略锁死 inline-style。在这种环境下硬推 CSS-in-JS,等于自掘坟墓。
所以我的建议很朴素:
| 场景 | 推荐方案 |
|---|---|
| 内部工具 / 快速原型 | CSS-in-JS(如 Emotion) |
| 面向公众的营销页 | CSS Modules + PostCSS |
| 政府/金融等强合规项目 | 传统 CSS + BEM |
| 团队有专职样式规范负责人 | 可尝试 Styled Components |
另外,别迷信“现代化”。我见过太多团队为了用 Tailwind 或 CSS-in-JS 而用,结果 build 时间翻倍,线上事故频发。技术选型不是追星,而是解决问题。
最终落地:混合策略才是王道
回到双11那个活动页,我们最终采用了“主框架用 CSS Modules,局部动态样式用内联 style”的混合方案。
为什么不用 CSS-in-JS?因为活动页要嵌入到主站 iframe 里,而主站 CSP 禁止动态插入 <style>。这个限制早在三个月前就定死了,但产品和设计根本不知道。
所以啊,技术决策不能只看代码,还得看组织流程。如果你公司像我们一样,前端、后端、运维、安全各自为政,那就别折腾太前沿的东西。稳字当头,先把需求按时交付再说。
当然,我也在悄悄学 Emotion。毕竟跳槽面试时,HR 总问“用过 CSS-in-JS 吗?” —— 虽然心里嘀咕“这玩意儿真有那么重要?”,但简历还是得写上。
写在最后:工程师的克制比热情更重要
作为每天和代码打交道的人,我理解大家对新工具的兴奋。但经历过几次线上样式崩坏、用户投诉、凌晨回滚之后,我越来越相信:最好的技术,是让问题消失的技术,而不是制造新问题的技术。
CSS-in-JS 不是银弹,传统 CSS 也不是古董。关键在于,你是否清楚自己在解决什么问题,以及你的团队能否承受随之而来的复杂度。
下次当你看到 GitHub 上某个 star 数破万的 CSS-in-JS 库时,不妨先问一句:“它能让我的代码更易读、更易维护吗?还是只是让我在简历上多一行 buzzword?”
毕竟,我们不是在写区块链智能合约,不需要每一行都炫技。用户只关心页面能不能点开,按钮有没有点歪——至于你是用 .button-primary 还是 css\color: red``,他们根本不在乎。
搞定需求,按时下班,这才是真正的“现代前端开发”。

评论 0