react学习4:CSS Modules 样式

本文声明是把光神的文章直接抄过来的,只是想自己看的时候方便一些,没其他目的。

react 每个组件里有 js 逻辑和 css 样式。js 逻辑是通过 es module 做了模块化的,但是 css 并没有。所以不同组件样式都在全局,很容易冲突

那 css 如何也实现像 js 类似的模块机制呢?

最容易想到的是通过命名空间来区分。

比如 aaa 下面的 bbb 下的 button,就可以加一个 aaa__bb__btn 的 class。而 ccc 下的 button,就可以加一个 ccc__btn 的 class。

常用的 BEM 命名规范就是解决这个问题的。BEM 是 block、element、modifier 这三部分:

  • 块(Block):块是一个独立的实体,代表一个可重用的组件或模块。

块的类名应该使用单词或短语,并使用连字符(-)作为分隔符。例如:.header、.left-menu。

  • 元素(Element):元素是块的组成部分,不能独立存在。

元素的类名应该使用双下划线(__)作为分隔符,连接到块的类名后面。例如:.left-menu__item、.header__logo。

  • 修饰符(Modifier):修饰符用于描述块或元素的不同状态或变体,用来更改外观或行为。

修饰符的类名应该使用双连字符(--)作为分隔符,连接到块或元素的类名后面。例如:.left-menu__item--active.header__logo--small

但是,BEM 规范毕竟要靠人为来约束,不能保证绝对不会冲突。所以最好是通过工具来做模块化,比如 CSS Modules

通过如下步骤创建一个react项目:

js 复制代码
npx create-vite

添加两个组件 Button1、Button2

js 复制代码
// Button1.tsx
import './Button1.css';

export default function() {
    return <div className='btn-wrapper'>
        <button className="btn">button1</button>
    </div>
}

// Button1.css
.btn-wrapper {
    padding: 20px;
}

.btn {
    background: blue;
}

// Button2.tsx
import './Button2.css';

export default function() {
    return <div className='btn-wrapper'>
        <button className="btn">button2</button>
    </div>
}

// Button2.css
.btn-wrapper {
    padding: 10px;
}

.btn {
    background: green;
}

渲染出来是这样的:

很明显,是样式冲突了:

这时候可以改下名字,把 Button1.css 改为 Button1.module.css:

现在就不会样式冲突了。为什么呢?

可以看到,button1 的 className 变成了带 hash 的形式,全局唯一的,自然就不会冲突了。

这就是 css modules。那它是怎么实现的呢?看下编译后的代码就明白了:

它通过编译给 className 加上了 hash,然后导出了这个唯一的 className。

所以在对象里用的,就是编译后的 className。

在 vscode 里安装 css modules 插件, 就可以提示出 css 模块下的 className 了

其实 vue 里也有类似的机制,叫做 scoped css:

js 复制代码
<style scoped> 
.guang { 
    color: red; 
} 
</style>  
<template>  
    <div class="guang">hi</div>  
</template>

会被编译成:

js 复制代码
<style> 
.guang[data-v-f3f3eg9] 
{ 
    color: red; 
} 
</style> 
<template> 
    <div class="guang" data-v-f3f3eg9>hi</div> 
</template>

通过给 css 添加一个全局唯一的属性选择器来限制 css 只能在这个范围生效,也就是 scoped 的意思。

它和 css modules 还不大一样,css modules 是整个 clasName 都变了,所以要把 className 改成从 css modules 导入的方式:

而 scoped css 这种并不需要修改 css 代码,只是编译后会加一个选择器

两者的使用体验有一些差别。

当然,在 vue 里可以选择 scoped css 或者 css modules,而在 react 里就只能用 css modules 了。

css modules 是通过 postcss-modules 这个包实现的,vite 也对它做了集成。

我们可以在 vite.config.ts 里修改下 css modules 的配置:

js 复制代码
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react()],
  css: {
    modules: {
      generateScopedName: "guang_[name]__[local]___[hash:base64:5]"
    }
  }
})

比如通过 generateScopedName 来修改生成的 className 的格式:

那如果在 Button1.module.css 里想把 .btn-wrapper 作为全局样式呢?

可以看到,现在编译后的 css 里就没有对 .btn-wrapper 做处理了:

只不过,因为 global 的 className 默认不导出,而我们用 styles.xxx 引入的,所以 className 为空:

这时候,或者把 className 改为这样:

或者在配置里加一个 exportsGlobals:true:

js 复制代码
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react()],
  css: {
    modules: {
      exportGlobals: true
    }
  }
})

可以看到,现在 global 样式也导出了。

还有一个配置比较常用,就是 localsConvention,当 localsConvention 改为 camelCase 的时候,导出对象的 key 会变成驼峰的:

这些就是 css modules 相关的配置了。

还有一点要注意的是,scss的global是这样写:

js 复制代码
.btn {
    :global {
        .xxx {
            background: blue;
            .yyy {
                color: #000;
            }
        }
    }
}

而CSS Modules 中是这样的:global()

js 复制代码
.div {
    :global(.text)  {
        color: red;

        &:hover {
            color: green;
        }  
    }
}
相关推荐
layman052818 小时前
webpack5 css-loader:从基础到原理
前端·css·webpack
半桔18 小时前
【前端小站】CSS 样式美学:从基础语法到界面精筑的实战宝典
前端·css·html
AI老李18 小时前
PostCSS完全指南:功能/配置/插件/SourceMap/AST/插件开发/自定义语法
前端·javascript·postcss
_OP_CHEN18 小时前
【前端开发之CSS】(一)初识 CSS:网页化妆术的终极指南,新手也能轻松拿捏页面美化!
前端·css·html·网页开发·样式表·界面美化
啊哈一半醒18 小时前
CSS 主流布局
前端·css·css布局·标准流 浮动 定位·flex grid 响应式布局
PHP武器库18 小时前
ULUI:不止于按钮和菜单,一个专注于“业务组件”的纯 CSS 框架
前端·css
电商API_1800790524719 小时前
第三方淘宝商品详情 API 全维度调用指南:从技术对接到生产落地
java·大数据·前端·数据库·人工智能·网络爬虫
晓晓莺歌19 小时前
vue3某一个路由切换,导致所有路由页面均变成空白页
前端·vue.js
Up九五小庞19 小时前
开源埋点分析平台 ClkLog 本地部署 + Web JS 埋点测试实战--九五小庞
前端·javascript·开源
摘星编程20 小时前
React Native + OpenHarmony:UniversalLink通用链接
javascript·react native·react.js