Vite 中 CSS 模块的核心是 “约定 + 语法” 区分全局 / 局部:
.module.* 后缀的文件,默认所有类名都是局部的(自动生成唯一哈希类名)。.module 后缀的文件,所有样式都是全局的(类名不做哈希处理)。
冲突的本质是 “边界模糊”,解决思路就是清晰划分两类样式的使用场景,再通过语法 / 配置兜底。这是最直观、低成本的方式,通过文件名约定区分全局 / 局部样式,从根源避免冲突。
.module 后缀的文件(如 src/styles/global.scss),存放全局通用样式(重置样式、主题色、全局组件样式)。scss
// global.scss(全局样式) * { margin: 0; padding: 0; box-sizing: border-box; } :root { --primary-color: #1890ff; } .global-btn { padding: 8px 16px; border: none; border-radius: 4px; background: var(--primary-color); color: white; }
.module.* 后缀的文件(如 src/components/Button/Button.module.scss),仅作用于当前组件。scss
// Button.module.scss(局部样式) .btn { border: 1px solid #eee; border-radius: 4px; padding: 6px 12px; }
main.jsx/main.tsx)中一次性导入,全局生效:jsx
// main.jsx import React from 'react'; import ReactDOM from 'react-dom/client'; import './styles/global.scss'; // 导入全局样式 import App from './App'; ReactDOM.createRoot(document.getElementById('root')).render(<App />);
如果需要在同一个模块化样式文件中混合全局 / 局部样式,使用 :global() 和 :local() 语法明确区分(Vite 基于 CSS Modules 规范支持该语法)。
scss
// Card.module.scss
// 1. 局部样式(默认,可省略 :local())
:local(.card) {
width: 300px;
padding: 20px;
border: 1px solid #eee;
}
// 2. 全局样式(:global() 包裹)
:global(.card-global) {
width: 400px;
padding: 20px;
border: 1px solid #ccc;
}
// 3. 嵌套场景:局部样式内的全局类名
:local(.card) {
:global(.title) { // 仅 .card 下的 .title 是全局类名
font-size: 18px;
color: #333;
}
.content { // 嵌套的局部类名(等价于 :local(.content))
font-size: 14px;
color: #666;
}
}jsx
// Card.jsx
import React from 'react';
import styles from './Card.module.scss';
export default function Card() {
return (
<>
{/* 局部样式:使用模块化对象 */}
<div className={styles.card}>
<h2 className="title">全局 title</h2>
<div className={styles.content}>局部 content</div>
</div>
{/* 全局样式:直接使用类名 */}
<div className="card-global">全局卡片</div>
</>
);
}通过 Vite 配置,精准控制哪些文件启用 CSS 模块,避免全局样式被误模块化,或局部样式污染全局。
javascript
运行
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
css: {
modules: {
// 1. 仅匹配 .module.* 后缀的文件启用模块化(默认规则,显式声明更清晰)
regexp: /\.module\.(css|scss|less)$/i,
// 2. 忽略特定全局样式文件(优先级高于 regexp)
ignore: /(global|reset)\.(css|scss|less)$/i,
// 3. 自定义类名生成规则,降低全局/局部类名撞名概率
generateScopedName: '[name]__[local]___[hash:base64:6]',
},
},
});regexp:仅让 .module.css/scss/less 文件启用模块化,其他文件默认全局。ignore:强制指定的全局样式文件(如 global.scss、reset.less)跳过模块化,即使文件名符合 regexp 也无效。generateScopedName:生成更长的哈希类名(如 Card__card___123abc),避免局部类名和全局类名 “巧合撞名”。如果全局样式和局部样式优先级冲突(比如全局样式覆盖了局部样式),可通过 CSS 优先级规则解决:
!important(谨慎使用,仅应急):scss
// Button.module.scss .btn { color: red !important; // 强制覆盖全局样式 }
表格
冲突场景 | 原因 | 解决方案 |
|---|---|---|
全局样式被局部样式覆盖 | 局部样式选择器权重更高 | 1. 提升全局样式权重;2. 局部样式避免过度嵌套 |
局部样式污染全局 | 误将 .module 文件的类名直接作为字符串使用 | 1. 确保局部样式通过 styles.xxx 访问;2. 检查是否漏写 .module 后缀 |
同一类名在全局 / 局部重复 | 类名命名太简单(如 .container) | 1. 局部类名结合组件名(如 .cardContainer);2. 自定义 generateScopedName 增加哈希长度 |
TypeScript 报错 “找不到全局类名” | 类型声明仅识别模块化类名 | 全局类名无需通过 styles 访问,直接写字符串(如 className="global-btn") |
.module 后缀)区分全局 / 局部样式,入口文件导入全局样式,组件内导入局部样式。:global() 声明全局样式、:local() 声明局部样式,明确边界。css.modules.regexp/ignore 精准控制模块化范围,generateScopedName 降低类名冲突概率。!important,优先通过选择器权重调整样式优先级。原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。