直接import './Button.css'会全局污染,因构建工具将其注入<head导致类名冲突;正确做法是用Button.module.css配合import styles from方式,通过styles.button获取哈希类名实现作用域隔离。

直接 import CSS 文件在 Preact 中可行,但默认不带作用域隔离,容易污染全局样式;用 .module.css 是更安全、更推荐的起点。
为什么 import './Button.css' 会出问题?
Preact 本身不处理 CSS,它依赖构建工具(如 Vite、Webpack 或 esbuild)来解析 import。如果你直接 import './Button.css',效果等同于在 HTML 中插入 <style> 标签——所有类名(比如 .btn)变成全局可用,一旦多个组件都定义了 .btn,就会互相覆盖。
- 常见错误现象:
className="btn"在 A 组件里是蓝色,在 B 组件里突然变灰色,查不到源头 - 构建时不会报错,但运行时样式错乱,尤其在 SSR 或多入口场景下更难排查
- Vite 默认启用 CSS Modules,但只对
.module.css生效;.css文件仍走全局路径
如何正确启用 CSS Modules?
关键不是改 Preact 配置,而是确保构建工具识别并编译 .module.css 文件。以主流工具为例:
- Vite:开箱即用,只要文件名是
Button.module.css,导入后import styles from './Button.module.css'就能拿到哈希化类名对象 - Webpack:需确认
css-loader配置中modules: { auto: /\.module\.css$/ }已启用,否则.module.css会被当普通 CSS 处理 - esbuild + postcss:需额外插件(如
esbuild-plugin-postcss2),且必须显式指定modules: true选项才支持模块化
别漏掉这一步:className={styles.button} 才有效;写成 className="button" 就失效——模块化后原始类名已不存在。
能否混用全局 CSS 和 CSS Modules?
可以,但必须明确分工,否则调试成本飙升:
立即学习“前端免费学习笔记(深入)”;
- 全局 CSS 仅用于:重置样式(
normalize.css)、设计系统变量(:root { --primary: #007bff; })、第三方库(如import 'prismjs/themes/prism.css') - CSS Modules 用于:组件自身结构样式(布局、间距、颜色语义),避免
composes过度嵌套,复杂复用建议提成独立原子类或用clsx动态拼接 - 禁止行为:
import './global.css';后又在.module.css里写.button { composes: btn from '../global.css'; }——跨文件依赖会让构建产物不可预测,热更新也容易失步
Preact 中用 CSS-in-JS(如 Emotion)要注意什么?
Preact 对 CSS-in-JS 的兼容性比 React 更敏感,尤其涉及运行时注入和 SSR:
-
@emotion/css可用,但需确保jsxImportSource指向@emotion/react,否则cssprop 会报css is not defined - SSR 场景下,Emotion 的
CacheProvider必须包裹整个 Preact 应用根节点,且服务端要调用extractCritical提取样式字符串,否则首屏无样式 - 性能提示:Preact 体积小,但 Emotion 运行时约 8KB,若只用几处动态色,不如直接用 CSS Modules +
style={{ color: props.theme === 'dark' ? '#fff' : '#000' }}
真正容易被忽略的是:Preact 的 JSX 编译器(如 @preact/compat)对 style 属性的驼峰转换是自动的,但对 className 不做任何处理——所以你写 class="btn" 在 JSX 里根本不会生效,必须始终用 className。


















