想象你是「组件花园」小区的物业经理。小区里每一户都是一位前端组件——按钮家、导航栏家、卡片家。最初物业给大家发了一本《全小区统一装修手册》,也就是全局 CSS:一人刷错墙,整栋楼跟着变,颜色冲突、样式串味成了居民投诉榜第一名。后来市面上出现两套新玩法:一套叫「门牌式装修包」(CSS Modules),交房时每户材料上印好独立门牌号(哈希类名),你家刷蓝绝不影响邻居,而且材料是预制好的,现场零等待;另一套叫「智能装修管家」(CSS-in-JS),管家常驻你家,你喊一声「把客厅刷成珊瑚橙」,他现场调漆、现场施工,要多灵活有多灵活,但每次干活都要花时间,工具箱还占电梯空间(打进 JS 包)。作为物业经理,给整个小区定哪套方案,关系到每一户的体验和物业的维护成本。这篇文章就当你请来的「选型参谋」,把两套方案的原理、产物、开销、生态掰开揉碎讲清楚。
技术选型是前端工作中高杠杆的决策点。读懂 CSS Modules 与 CSS-in-JS 的底层取舍,能让你在架构评审时给出有数据、有依据的建议,也让未来接手项目的同事少踩几个坑。
目录
可视化演示
对比CSS Modules构建时哈希隔离与CSS-in-JS运行时注入的完整流程,展示构建产物、运行时开销和SSR兼容性差异,交互切换不同维度观察技术选型要点。
本节配有交互动效(进入视口后自动加载,约 3KB)。
CSS Modules 构建时隔离 构建时哈希化类名.button_abc123 产出纯CSS零运行时开销 .btn \u2192 .btn_a3f9 \u70b9\u51fb\u5207\u6362\u67e5\u770bCSS-in-JS\u65b9\u6848
1. 样式方案概览
还记得《全小区统一装修手册》的灾难吗?两家人同时把门牌号写成「101」,信件永远送错,你说的是按钮家的蓝色,隔壁卡片家也改了蓝。这个痛点催生了两种自救方案:一种是「门牌式装修包」(CSS Modules,指把 CSS 文件里的类名在构建期局部化的技术),另一种是「智能装修管家」(CSS-in-JS,指用 JavaScript 来书写并动态注入样式的方案统称)。它们都试图回答同一个问题:怎么让每家装修互不干扰,又足够灵活。
CSS Modules 的思路是「提前写好、按户编号」。借助 webpack 这样的构建工具,样式开发者在 .module.css 文件里写的 .btn,在经过 css-loader 的 modules 模式编译后,会自动变成 ._btn_1a2b3 这类带哈希的局部类名。HTML 里的引用也会同步替换,过程全部发生在打包阶段,运行时的浏览器完全无感知。
CSS-in-JS 的思路则相反——「现场生成、用完即走」。以 styled-components 为例,你在组件文件里写 const Button = styled.button 带样式模板,库会在组件首次渲染时把规则编译成一段 <style> 文本并注入页面。两者解决的是同一个问题:全局命名冲突,但一个把功夫花在构建期,一个把功夫花在运行时。
如果把小区比作前端项目,CSS Modules 相当于物业在交房前就把所有门牌印好,业主拎包入住;CSS-in-JS 则像每家配了位高级管家,随叫随到、按需施工。理解了这个起点,后面所有对比都顺理成章。
- file\_path(文件路径)样式文件所在的路径,相当于「楼栋号」,确保
button和modal两个文件里都有的.title各归各家。 - origin\_name(原始类名)开发者书写的原始类名,比如
btn,这是人类可读的语义名,也是冲突的根源。 - hash()(哈希函数)一种确定性的映射函数,相同的输入永远得到相同的输出,所以同一份源码每次构建出的类名一致,产物可复现。
- local\_class(局部类名)哈希后的最终门牌号,源码里的
.btn一旦完成构建,就再也找不到原始写法,冲突从根上被切断。
2. 作用域隔离
门牌式装修包之所以不怕串味,是因为它把名字本身改掉了——你家的「101」变成了「101-A-7F」,隔壁再怎么叫「101」也跟你无关。智能管家则是另一种玩法:他给每户门上贴临时贴纸,但这张贴纸只有你在家(组件渲染时)才存在,门一关就消失。两者都实现了「样式只对自家组件生效」,但机制完全不同。
CSS Modules 的作用域隔离靠「选择器重写」。构建工具扫描 CSS 文件,把每个类选择器展开成「哈希后的新选择器」,再同步改写 JSX 或模板里的 className。这是个纯文本级别的变换,不改写 CSS 语言的任何规则,因此浏览器解析产物时没有任何额外负担。
CSS-in-JS 的作用域隔离靠「动态注入」。样式规则不是预先存在于 CSS 文件里的,而是组件渲染时由 JavaScript 函数当场生成,紧接着插入一个专属的 <style> 标签或 hash 属性。规则离组件最近、随组件卸载而移除,天然不会泄漏到别处。代价是每次渲染都要经历「生成规则 → 注入文档」的完整链路。
回头看比喻:门牌号改了,但是门牌号对应的那扇门还是原来的门——CSS Modules 保持了「类名 ↔ 样式」的稳定映射;管家临时贴纸虽然灵活,但每次开门都要重贴一遍。理解了这个差异,你就抓住了两派最本质的分水岭。
- rewrite(selector)(改写选择器)CSS Modules 在构建期把
.btn改写成唯一哈希类名,源码里再也找不到原始选择器,从命名空间上杜绝冲突。 - inject(rule)(注入样式规则)CSS-in-JS 在运行时把
color: blue这类规则打包进动态<style>标签,规则的存在周期与组件生命周期绑定。 - cases(条件分支)两条分支说明没有唯一正确答案,只是把「隔离」这个目标拆成了「改名字」和「现做现卖」两种策略。
3. 构建产物对比
门牌式装修包交到你手上的,是一份完整的《预制建材清单》——所有材料在工厂里就打包好,快递送到现场直接施工。这份清单就是独立 .css 文件。智能管家的工具箱则不同:他把所有可能用到的工具都塞进电梯(JS bundle),你每上下楼一次,都得为这份重量买单。
CSS Modules 的构建产物就是普通的静态 CSS 文件。它可以在浏览器里被当作文本快速解析,可以交给 CDN 缓存,发布后只要类名不变就几乎不会变动。更重要的是,CSS 文件与 JavaScript 文件在浏览器中是两条独立的加载通道,CSS 不会阻塞 JS 执行,还可以利用 <link rel="stylesheet"> 的并发加载能力。
CSS-in-JS 的产物则全部收编进 JavaScript。样式模板以字符串形式存在于组件源码中,打包后成为 bundle 的一部分。浏览器需要先下载 JS、再解析执行、最后才能把样式注入页面。这带来两个连锁反应:一是首屏 CSS 的到达时间被 JS 的下载执行时间拖累;二是代码分割时,样式必须跟着组件 JS 一起加载,很难像独立 CSS 那样精细控制缓存粒度。
所以当你看到「CSS-in-JS 让首屏变慢」的说法,本质不是在黑某一种工具,而是在陈述一个体积模型上的事实:样式走 JS 通道,就必然被 JS 的加载和执行节奏拖住。如果你的项目对首屏和缓存极度敏感,这个对比就是关键决策依据。
- B\_base(基础框架体积)CSS-in-JS 运行时框架本身的大小,以 styled-components 为例约 12KB gzip,它是一切注入能力的地基。
- Σ s\_i(样式字符串之和)每个组件的样式模板字符串都会被打进 JS bundle,组件越多,bundle 膨胀越明显。
- C\_base(基础样式文件)CSS Modules 也有一份公共基础样式,但它独立成文件,可以被浏览器单独缓存。
- Σ c\_i(CSS 规则之和)每个组件对应的哈希类名 CSS 规则,因为有很多共享声明,实际增量远小于 JS 中的模板字符串累计量。
4. 运行时开销
管家接到「刷成珊瑚橙」的指令后,要经历「调漆—试色—上墙」三步。调漆是计算样式值,试色是解析 props,上墙是更新 <style> 标签。每一户装修时都要来一遍,户数越多,物业的整体响应越慢。而门牌式装修包的材料是预拌好的一桶漆,工人拎起来就刷,没有任何等待时间。
CSS-in-JS 的运行时可拆成三笔账。第一笔是函数执行:组件渲染时,样式函数跑一遍,处理条件 props、计算动态值;第二笔是序列化:把样式对象转换成 CSS 文本时需要进行字符串拼接和哈希生成;第三笔是 DOM 操作:样式类名或 <style> 节点发生变化时,浏览器要重新计算样式。这三笔在组件挂载、更新、卸载的每个生命周期里反复发生。
CSS Modules 的运行时开销则约等于零。组件拿到的是一串已经编译好的哈希类名,render 时把它拼进 className 即可。浏览器只需要在样式表里做一次类名匹配,没有任何动态计算。组件再多,也只是多一次查找,而不是多一次生成。
比作小区物业,就是「每位管家每户都调一次漆」和「中央厨房统一配好漆、施工队直接刷」的区别。CSS-in-JS 并非不能用,而是要把这笔账算进性能预算里,尤其当你有复杂的动态主题或成百上千个组件时。
- k(单组件样式生成耗时)包含插值计算、对象序列化、节点更新三步累积的单位耗时,量级通常在毫秒或亚毫秒。
- n\_mounted(已挂载组件数)页面中实际渲染出来的动态样式组件数量,100 个组件就是 100 次完整流程。
- 乘积关系总开销与组件数成正比,这也是为什么组件一多,交互卡顿的感受会指数级变明显。
5. 生态兼容性
小区要接入城市管网:门牌式装修包用的是标准建材,任何建筑队都能施工;智能管家则需要物业预先装好「对接盒」——在 React 项目里是 Provider 上下文,在 SSR 里则是样式抽取插件。这个比喻贯穿生态兼容性的全部讨论。
CSS Modules 是「框架无关」的。它本质上是 CSS 语言与构建工具的一次约定,React、Vue、Svelte、原生 JS 都能用。SSR 场景最简单:服务端渲染出 HTML,同时通过 <link> 把构建好的 CSS 文件带上,浏览器拿到的首屏就是「有样式的完整页面」,不需要额外处理。Vue 的 SFC 里 <style scoped> 还内置了类似的思路。
CSS-in-JS 则往往深度绑定框架。styled-components 依赖 React 的 Context 机制做主题传递,Emotion 同样围绕 React 生态设计。如果项目是 Vue,你得选 @vueuse/head 或 vue-styled-components 这类移植方案,体验总有落差。SSR 更是重灾区:样式在运行时才产生,服务端必须遍历组件树收集所有样式规则,内联进 HTML,否则浏览器先渲染无样式内容、JS 加载后才补上样式,造成 FOUC(无样式闪烁)。
生态兼容性说白了是「这套方案在你们小区外的世界走得通吗」。如果团队的技术栈很纯、基本锁死 React,CSS-in-JS 的绑定不是问题;如果项目未来可能换框架、做多端、重度依赖 SSR,那零依赖的 CSS Modules 会给你更多退路。
- render(App)(渲染组件)服务端把 React 组件树渲染成静态 HTML 字符串,完成首屏内容输出。
- collectStyles(App)(收集样式)服务端在渲染同时遍历组件树,把每个动态生成的样式规则收集起来,相当于把管家的调漆配方装进一个筐。
- 拼接输出把内容筐和样式筐一起塞进响应体,浏览器解析时才能「内容与样式同帧出现」,避免白屏闪烁。
6. 零运行时方案
能不能让管家把常点的色号提前做成预制料包,用户下单时拆包即用,把「调漆」的功夫全省了?这就是零运行时方案(如 Linaria、vanilla-extract)想做的事:保留「用 JS 写样式」的开发体验,却在构建期就把样式提取成静态 CSS,运行时不再有任何 JS 参与。
Linaria 的思路是「模板字符串静态分析」。你在组件里写 ` const title = csscolor: blue; ,Babel 插件在编译时解析这段模板,只要没有动态插值,就直接把 color: blue 提取进 CSS 文件,组件代码里只留一个哈希类名。vanilla-extract 更进一步,用 TypeScript 对象语法定义样式,编译时对整份样式表做类型检查,构建产物是标准的 .css.ts` 文件。
这类方案完美命中「既要又要」:开发体验接近 CSS-in-JS——可以写条件样式、做主题函数、获得类型安全;运行时性能接近 CSS Modules——产物是纯 CSS,零 JS 开销。代价是动态能力的边界被压缩:凡是「运行时才知道的值」,必须显式地通过 CSS 变量或 data 属性在运行时传入,不能再指望 JS 在渲染时现场计算。
如果把选型比作装修,零运行时方案就是「管家在工厂把料包做好,现场只负责拆包」——你要的颜色依然是管家配的,但配色的时间不再占用你家施工的时间。对于组件库作者和追求极致性能的团队,这个中间地带常常是更优解。
- styled\_templates(样式模板)开发者用 JS/TS 语法写的样式定义,例如
color: ${brandColor},这里的插值只允许编译期可知的常量。\text{styled\_templates} = \text{css\0} - compile()(编译提取)Babel 或 Vite 插件在构建期执行模板求值,把所有静态规则抽出来,相当于管家在工厂里预制料包。
\text{compile}(\text{css\0}) \rightarrow \text{'.\_title\_x9k2 { background: \#6366F1; }'} - styles.css(静态产物)编译输出与手写 CSS 文件无异,可以被缓存、被并发加载、被任何框架使用。
7. 原子化CSS搭配
物业在小区门口建了「公共建材市场」:标准色卡、标准门框、标准间距,随取随用。这就是原子化 CSS 的定位——以 Tailwind 为代表,提供 flex、text-sm、bg-brand 这类单一职责的低层级类,让开发者直接在 HTML 里组合出布局和视觉。很多人以为它和 CSS Modules 是敌人,其实它们是不同楼层的建材。
实践中两者可以清晰分层。Tailwind 负责「跨组件共用」的部分:间距、排版、通用布局、常用配色,这些往往占一个页面 70% 的视觉规则。CSS Modules 负责「组件专属」的部分:组件自身的结构布局、状态样式、不可复用的特殊视觉。最上层再用 CSS 变量统管主题与品牌色,实现暗黑模式或换肤。
分工边界一旦清楚,团队的协作规范也随之明确:所有人在 Tailwind 里优先选现成令牌,不造新词;组件内部结构一律写进局部 CSS;主题色必须走 CSS 变量,禁止在原子类里写死色值。这样既避免了「类名爆炸」,也防止了「全局样式失控」,把两种方案的优点都吃到。
把小区建得漂亮,从来不是「只用一种建材」。公共建材市场解决通用需求,门牌式装修包满足定制要求,主题变量则是连接两者的「色卡中枢」。三者各司其职,就是当下大型项目最稳的样式架构。
- Atomic(原子类占比)Tailwind 类承接通用布局与配色,类名可复用、体积固定,不随组件增长而膨胀。
- Local(局部类占比)CSS Modules 类承接组件专属结构,带有哈希作用域,避免组件间的意外串扰。
- Var(主题变量占比)CSS 变量统一收口品牌色、圆角、阴影等主题令牌,运行时切换类名或
data-theme即可换肤。
8. 选型决策指南
选装修方案要看楼型:出租公寓(内部管理系统)求稳、样板间(营销活动页)求炫、高端住宅(开源组件库)求极致。没有哪套方案通吃,关键是把项目的三个参数代入决策函数:规模、主题需求、团队背景。
如果你的项目是重 SSR 的大型中后台,追求首屏速度和缓存友好,CSS Modules 或零运行时方案是稳健起点。如果项目是强运营的营销页,主题和个性化是命根子,CSS-in-JS 的动态能力让设计师天马行空的想法能快速落地,但要搭配性能预算和 memo 优化。如果是发布给全世界的组件库,请毫不犹豫选择零运行时——你的用户不该为你的便利支付运行时成本。
最后看团队:全员 React 深度使用者,styled-components 的生态熟稔,CSS-in-JS 的学习曲线不是问题;团队更偏传统 CSS 背景,CSS Modules + Tailwind 的门槛更低。技术选型没有银弹,只有「基于现状做加权评分」的工程决策。
回到「组件花园」小区:没人能替你决定要不要请管家,但当你把楼型、住户诉求、物业团队的能力都列清楚,答案会自然浮现。记住两条底线——别让样式架构拖垮首屏,也别忘了换肤和个性化是运营的命脉。
- size(规模变量)组件总量、迭代频率、参与人数。规模越大,越需要构建期确定性和可缓存性。
- theme(主题需求)动态换肤、品牌个性化、运行时配置样式的强度。越强,越需要 CSS-in-JS 的灵活性或 CSS 变量的桥接。
- team(团队背景)团队对 React 生态的深度、对构建链路的掌控力,决定了新方案的学习与维护成本。
- g()(决策函数)把三个变量摆上桌面,给每个维度分配权重,算出最适合你项目的组合。
AI 应用场景
场景一:中后台管理系统技术选型
阶段:前端工程化方案设计
解决的问题:传统全局 CSS 在 8 人并行开发的团队里极易发生样式冲突——你改的 .table 影响了我做的页面。CSS Modules 把每个组件的类名局部化后,开发者可以放心命名,不必担心「撞车」;配合 CSS 变量实现暗黑模式切换,避免运行时开销。就像给每户门牌加了哈希前缀,再在楼顶装一套统一色卡系统。
具体案例:某 SaaS 公司重构 200+ 组件的管理后台,选定「CSS Modules + CSS 变量」组合。改造后样式冲突工单从每周 15 条降到 0 条,夜间模式上线只需在根节点切换 data-theme 属性,无需重新构建皮肤包。
场景二:强运营的营销活动页
阶段:前端渲染性能优化
解决的问题:营销页最大的痛点是「每个活动一套视觉」,且运营在后台配置品牌色后要立即生效。CSS-in-JS 能直接把配置作为 props 传入样式组件,按需生成色板;搭配组件级 memo 后,运行时开销可以控制在可接受范围。就像样板间的智能管家,为每场活动现场调出专属配色。
具体案例:某电商平台大促期间上线 50 个活动页,每个页面由运营配置主色和辅助色。团队用 styled-components 实现 ThemeProvider 动态注入色板,页面渲染速度满足 2 秒标准,视觉个性化需求零改版上线。
场景三:开源组件库发布
阶段:构建产物交付优化
解决的问题:第三方组件库如果用 CSS-in-JS,会把整个运行时框架强制塞进用户的项目里,用户为自己的「方便」埋单,体验很差。零运行时方案(vanilla-extract)在编译期产出纯 CSS,用户拿到的是干净静态文件,既保留类型安全又节省体积。就像物业把预制料包交给住户,住户自己拆包就行,不用连管家一起聘用。
具体案例:某团队用 vanilla-extract 重写组件库,发布后在 unpkg 上的包体积从 45KB 降至 8KB,接入方反馈「不再需要在 Next.js 里配置 styled-components 的 SSR 插件」。
Python 演示
"""对比 CSS Modules 与 CSS-in-JS 两种样式方案的开销模型(模拟数据)。""" BASE_CSS_KB = 8 # CSS Modules 基础样式体积BASE_JS_KB = 120 # CSS-in-JS 运行时框架体积CSS_PER_COMP = 0.3 # 每组件新增 CSS 量(KB)JS_PER_COMP = 1.2 # 每组件打入 JS 的样式代码量(KB)RUNTIME_PER_COMP = 0.08 # 每组件运行时计算耗时(ms) def estimate_modules(components): return { "css_kb": round(BASE_CSS_KB + components * CSS_PER_COMP, 1), "js_kb": 0, "runtime_ms": 0.0, } def estimate_css_in_js(components): return { "css_kb": 0, "js_kb": round(BASE_JS_KB + components * JS_PER_COMP, 1), "runtime_ms": round(components * RUNTIME_PER_COMP, 2), } def main(): print("组件数 | CSS Modules | CSS-in-JS") print("--------+-----------------------+---------------------") for n in (10, 50, 100, 500): m = estimate_modules(n) j = estimate_css_in_js(n) print( f"{n:6d} | css {m['css_kb']:6.1f}KB / 0ms | " f"js {j['js_kb']:6.1f}KB / {j['runtime_ms']:5.2f}ms" ) if __name__ == "__main__": main()
BASE_JS_KB = 120定义 CSS-in-JS 运行时框架的固定负担,这是它起跑就落后的部分。CSS_PER_COMP = 0.3每个组件带来的 CSS 增量很小,因为多个组件的共享声明会被合并去重。def estimate_modules(components):封装 CSS Modules 的估算逻辑,它只产出 CSS 体积,没有 JS 体量和运行时耗时。"runtime_ms": 0.0这行直接写死为 0,强调 CSS Modules 最硬的优点:运行时不参与任何计算。round(components * RUNTIME_PER_COMP, 2)CSS-in-JS 的运行时开销与组件数成正比,模型里用线性关系表达。__name__ == "__main__"程序入口,保证脚本可以直接运行,输出可对比的表格。
常见误区
误区不就是把 CSS 写进 JS 里了吗?多打包几十 KB 而已,渲染时浏览器该咋画还咋画,性能应该差不多吧。
正解差别在「样式是什么时候、由谁生成的」。CSS Modules 的类名在构建期就定死了,运行时只剩字符串查找;CSS-in-JS 每次组件渲染都要执行样式函数、序列化样式对象、更新 <style> 节点。当页面有几百个动态样式组件时,这不再是「几十 KB」的问题,而是每帧都多出来的计算量。就像一个管家每顿饭都现场调酱汁,另一个直接用中央厨房的预制酱包,后厨的忙闲程度完全不同。
为何常见:小项目里组件少,性能差异在 DevTools 里几乎测不出来,容易让人以为两种方案开销相同。
误区类名都被哈希了,写死在 CSS 文件里,用户点一下切换暗黑模式,这怎么可能实现?
正解CSS Modules 只管「类名作用域」,不管「样式值」。具体颜色完全可以交给 CSS 变量:.button { color: var(--text-primary); } 只需要切换根节点的 data-theme 属性,--text-primary 的值就会全局更新。门牌号不变,刷漆用公共色卡换色即可。
为何常见:把「类名隔离」误当成「样式值冻结」,忽略了 CSS 变量这个天然的运行时桥梁。
误区一个页面里既有 `flex justify-between` 又有 `styles.header__title`,类名混在一起不乱吗?肯定只能选一种。
正解两者解决的问题层级不同。Tailwind 管跨组件的通用布局和配色,CSS Modules 管组件专属结构,CSS 变量管主题。实践中「七成原子类 + 三成局部类」的分层非常常见,只要团队约定好边界,就不会乱。就像小区既有公共建材市场,也有每户的自选装修包,二者服务不同的装修需求。
为何常见:框架之争让很多人默认「选 A 就必须扔掉 B」,忽视了工程方案本来就是可以组合的。
误区服务端渲染返回的 HTML 里应该已经带上了样式吧?毕竟服务器也执行了一遍组件代码。
正解CSS-in-JS 默认只在浏览器端注入样式。SSR 时必须在服务端遍历组件树、收集样式规则、内联到 HTML,否则浏览器先渲染无样式的首屏、JS 加载后才补上样式,造成 FOUC(无样式闪烁)。这需要 babel-plugin 或样式抽取 API 的额外配置。
为何常见:本地开发几乎都是纯客户端渲染,很少模拟 SSR 验证,很多团队直到上线压测才发现首屏样式闪一下的问题。
面试题
Q1. CSS Modules 的局部类名通过什么机制隔离冲突?
- A.浏览器 Shadow DOM 自动隔离
- B.构建期哈希改写原类名← 正确答案
- C.运行时注入唯一 style 标签
- D.CSS 变量实现命名空间
答案:B。CSS Modules 的核心是构建工具(如 css-loader)将 .btn 改写为带哈希的局部类名,从命名上杜绝冲突。Shadow DOM 是浏览器原生隔离方案,与 CSS Modules 无关;运行时注入 style 标签是 CSS-in-JS 的做法;CSS 变量只管值,不管命名空间。
Q2. CSS Modules 构建后的产物形态是什么?
- A.独立静态 CSS 文件← 正确答案
- B.嵌入组件 JS 的字符串
- C.运行时生成的 style 标签
- D.浏览器扩展注入的样式
答案:A。CSS Modules 在构建期把哈希类名和样式规则输出为独立 .css 文件,浏览器可直接加载并缓存。B、C 描述的是 CSS-in-JS 的产物与运行方式,D 与主题无关。
Q3. CSS-in-JS 样式生效的时机是何时?
- A.构建时编译为 CSS 文件
- B.浏览器运行时注入节点← 正确答案
- C.服务端渲染时静态输出
- D.开发者保存代码时生成
答案:B。CSS-in-JS 的样式规则在组件渲染时由 JS 计算生成,并注入 <style> 标签,因此生效时机是浏览器运行时。构建期和服务端渲染都是 CSS Modules 或零运行时方案的特征。
Q4. 零运行时方案(如 Linaria)的核心思路是什么?
- A.运行时用 Worker 压缩样式字符串
- B.构建期把样式提取成静态 CSS 文件← 正确答案
- C.每次渲染都动态计算样式对象
- D.用 CSS 变量在运行时解析全部
答案:B。Linaria 通过在构建期对样式模板做静态分析,把可确定的部分提取为纯 CSS 文件,JS 运行时不再参与样式生成,从而兼具开发体验与性能。A、C 仍是运行时开销,D 是把负担转嫁给 CSS 变量解析。
Q5. SSR 场景下 CSS-in-JS 额外要做哪件事?
- A.在服务端抽取样式并内联← 正确答案
- B.让浏览器禁用全部 JS 水合
- C.将样式统一转成 CSS 变量
- D.把样式表托管到 CDN 加速
答案:A。CSS-in-JS 的样式在客户端运行时才产生,SSR 时必须用插件在服务端收集所有样式规则,内联进返回的 HTML,否则首屏会出现无样式闪烁(FOUC)。禁用水合会毁掉交互,转 CSS 变量和 CDN 都不是问题的关键。
Q6. CSS Modules 相对 CSS-in-JS 的优势有哪些?
- A.运行时开销几乎为零← 正确答案
- B.构建产物可独立缓存← 正确答案
- C.天然支持样式代码分割← 正确答案
- D.开箱实现动态主题换肤
答案:ABC。CSS Modules 的类名构建期确定,产物是独立 CSS 文件,浏览器可单独缓存,也天然跟着组件做代码分割。但动态换肤并不是它的开箱能力,需要配合 CSS 变量才能实现,所以 D 不正确。
Q7. 需要动态换肤时,CSS Modules 的最佳搭档是?
- A.配置 CSS 变量统一主题← 正确答案
- B.为每套皮肤生成独立 CSS
- C.用更高优先级全局样式覆盖
- D.在运行时向头部追加 style 标签
答案:A。CSS 变量(custom properties)可以在根节点切换 data-theme 时整体更新品牌色,类名无需变化,是 CSS Modules 实现换肤的最自然桥梁。为每套皮肤生成独立 CSS 会导致构建产物爆炸,全局覆盖和运行时追加标签都会绕回样式失控的老路。
Q8. 以下哪些属于 CSS-in-JS 的运行时开销?
- A.样式对象的序列化计算← 正确答案
- B.组件渲染时函数插值计算← 正确答案
- C.动态 style 标签的节点更新← 正确答案
- D.CSS 文件请求的阻塞时间
答案:ABC。CSS-in-JS 在运行时需要把样式对象序列化为 CSS 文本、执行带插值的样式函数、并更新 <style> 节点,这三项都是它的固有开销。CSS 文件请求阻塞是「样式走独立 CSS 文件」才会有的问题,属于 CSS Modules 的讨论范畴。
Q9. vanilla-extract 属于哪一类样式方案?
- A.构建期类型安全的样式方案← 正确答案
- B.纯运行时注入的样式框架
- C.基于 Shadow DOM 的样式方案
- D.面向 Tailwind 的原子类框架
答案:A。vanilla-extract 用 TS 对象语法定义样式,在构建期编译为静态 CSS,同时提供完整的类型推导,是零运行时方案的代表。B 是 styled-components 的路线,C 是 Web Components 的机制,D 是另一个维度的工具。
Q10. 大型项目混用 Tailwind 与 CSS Modules,合理的分层是?
- A.Tailwind 负责通用布局与常用配色← 正确答案
- B.CSS Modules 负责组件专属的结构样式← 正确答案
- C.CSS 变量统一管理主题与品牌色← 正确答案
- D.所有样式统一进入全局 CSS 文件
答案:ABC。成熟的混用策略是分层治理:Tailwind 原子类承接约七成通用规则,CSS Modules 局部类承接组件特有结构,CSS 变量在最上层统管主题令牌。把一切写进全局 CSS 会回到样式冲突的原点,也是最早被否定的方案。