Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >React 进阶 - 模块化 CSS

React 进阶 - 模块化 CSS

作者头像
Cellinlab
发布于 2023-05-17 12:49:44
发布于 2023-05-17 12:49:44
2K00
代码可运行
举报
文章被收录于专栏:Cellinlab's BlogCellinlab's Blog
运行总次数:0
代码可运行

# 模块化 CSS 的作用

随着 React 项目日益复杂化、繁重化,React 中 css 面临很多问题,比如样式类名全局污染、命名混乱、样式覆盖等。这时, css 模块化就显得格外重要。

css 模块化的几个重要作用:

  • 防止全局污染,样式被覆盖
    • 如果不规范 css 的话,这种情况在实际开发中会变得更加棘手,有时候甚至不得不用 !important 或者 行内样式 来解决
    • Web Components 标准中的 Shadow DOM 能彻底解决这个问题,但它的做法有点极端,样式彻底局部化,造成外部无法重写样式,损失了灵活性
  • 解决命名混乱
    • 没有 css 模块化和统一的规范,会使得多人开发,没有一个规范
  • 减少 css 代码冗余,体积庞大
    • React 中各个组件是独立的,所以导致引入的 css 文件也是相互独立的,比如在两个 css 中,有很多相似的样式代码,如果没有用到 css 模块化,构建打包上线的时候全部打包在一起,那么无疑会增加项目的体积

React 使用 css 模块化的思路:

  • css module ,依赖于 webpack 构建和 css-loader 等 loader 处理,将 css 交给 js 来动态加载
  • 直接放弃 css ,css in js 用 js 对象方式写 css ,然后作为 style 方式赋予给 React 组件的 DOM 元素,这种写法将不需要 .css .less .scss 等文件,取而代之的是每一个组件都有一个写对应样式的 js 文件

# CSS Modules

css Modules ,使得项目中可以像加载 js 模块一样加载 css ,本质上通过一定自定义的命名规则生成唯一性的 css 类名,从根本上解决 css 全局污染,样式覆盖的问题。对于 css modules 的配置,推荐使用 css-loader,它对 CSS Modules 的支持最好,而且很容易使用。

css-loader 配置:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
{
  test: /\.css$/,
  use: [
    'css-loader?modules'
  ]
}

css 文件:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
.title {
  color: red;
}

js 文件:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import style from './index.css'

const App = () => {
  return (
    <div className={style.title}>Hello World</div>
  )
}

# 自定义命名规则

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
{
  test: /\.css$/,
  use: [
    {
      loader: 'css-loader',
      options: {
        modules: {
          localIdentName: '[path][name]__[local]--[hash:base64:5]'
        }
      }
    }
  ]
}

  • [path][name]__[local] -> 开发环境,便于调试,可以直接找到此类名对应的文件
  • [hash:base64:5] -> 生产环境,便于生产环境压缩类名

# 全局变量

一旦经过 css modules 处理的 css 文件类名 ,再引用的时候就已经无效了。因为声明的类名,比如如上的 .text 已经被处理成了哈希形式。

那么怎么样快速引用声明好的全局类名呢?CSS Modules 允许使用 :global(.className) 的语法,声明一个全局类名。凡是这样声明的 class ,都不会被编译成哈希字符串。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
.title {
  color: red;
}
:global(.title_bg) {
  background-color: pink;
}

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import style from './index.css'

const App = () => {
  return (
    <div className={`${style.title} title_bg`}>Hello World</div>
  )
}

CSS Modules 还提供一种显式的局部作用域语法: local(.title) 等同于 .title

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
.title {
  color: red;
}

/* 等同于 */
:local(.title) {
  color: red;
}

# 组合样式

CSS Modules 提供了一种 composes 组合方式,实现对样式的复用:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
.base {
  color: blue;
}
.title {
  composes: base; /* 继承 base 样式 */
  font-size: 20px;
}

composes 可以将多个 class 类名添加到元素中。composes 还有一个更灵活的方法,支持动态引入别的模块下的类名。

style1.css:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
.base {
  color: red;
}

style2.css:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
.title {
  composes: base from './style1.css';
  font-size: 20px;
}

# 配置 less 和 sass

less webpack 配置

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
{
  test: /\.less$/,
  use: [
    {
      loader: 'css-loader',
      options: {
        modules: {
          localIdentName: '[path][name---[local]---[hash:base64:5]'
        }
      }
    },
    'less-loader'
  ]
}

  • index.less:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
.text {
  color: blue;
  background-color: pink;
}

  • index.js:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import style from './index.less'

const App = () => {
  return (
    <div className={style.text}>Hello World</div>
  )
}

# 组合方案

正常情况下,React 项目可能在使用 css 处理样式之外,还会使用 scss 或者 less 预处理。

  • 可以约定对于全局样式或者是公共组件样式,可以用 .css 文件 ,不需要做 CSS Modules 处理,这样就不需要写 :global 等繁琐语法
  • 对于项目中开发的页面和业务组件,统一用 scss 或者 less 等做 CSS Module,也就是 css 全局样式 + less / scss CSS Modules 方案

这样就会让 React 项目更加灵活的处理 CSS 模块化。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import React from 'react';

import style from './index.less';

export default () => {
  return <div>
    <button className='search-btn'>Search</button>
    <div className={style.text}>Less + CSS Modules</div>
  </div>
}

# 动态添加 class

CSS Modules 可以配合 classNames 库 实现更灵活的动态添加类名。

index.less

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
.base {}
.dark {
  background-color: #000;
  color: #fff;
}
.light {
  background-color: #fff;
  color: #000;
}

index.jsx

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import cx from 'classnames';

import style from './index.less';

export default function Index() {
  const [theme, setTheme] = useState('dark');
  return <div>
    <button
      className={cx(style.base, style[theme])}
      onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')}
    >
      Change Theme
    </button>
  </div>;
}

# CSS Modules 优点及注意事项

  • 优点
    • CSS Modules 的类名都有自己的私有域的,可以避免类名重复/覆盖,全局污染问题
    • 引入 css 更加灵活,css 模块之间可以互相组合
    • class 类名生成规则配置灵活,方便压缩 class 名
  • 注意事项
    • 仅用 class 类名定义 css ,不使用其他选择器
    • 不要嵌套 css .a{ .b{} } 或者重叠 css .a .b {}

# CSS IN JS

# 基础使用

CSS IN JS 相比 CSS Modules 更加简单, CSS IN JS 放弃 css ,用 js 对象形式直接写 style 。

index.jsx

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import React from 'react';
import Style from './style';

export default function Index() {
  return <div style={Style.boxStyle}>
    <span style={Style.textStyle}>CSS in JS</span>
  </div>;
}

style.js

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const boxStyle = {
  backgroundColor: '#000',
};

const textStyle = {
  color: '#fff',
};

export default {
  boxStyle,
  textStyle,
};

# 灵活运用

由于 CSS IN JS 本质上就是运用 js 中对象形式保存样式, 所以 js 对象的操作方法都可以灵活的用在 CSS IN JS 上。

拓展运算符实现样式继承

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const baseStyle = {
  color: '#fff',
  fontSize: '20px',
};
const containerStyle = {
  ...baseStyle,
  backgroundColor: '#000',
};

动态添加样式变得更加灵活

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const dark = {
  backgroundColor: '#000',
};
const light = {
  backgroundColor: '#fff',
};

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import React from 'react';
import Style from './style';

const Index = () => {
  return <div style={theme === 'light' ? Style.dark : Style.light }>
    CSS in JS
  </div>;
}

# styled-components 库

CSS IN JS 也可以由一些第三方库支持,如 styled-components, 可以把写好的 css 样式注入到组件中,项目中应用的已经是含有样式的组件。

基础用法

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import React from 'react';
import styled from 'styled-components';

/* 给 button 标签添加样式,形成 Button React 组件 */
const Button = styled.button`
  background: #6a8bad;
  color: #fff;
  border: none;
  padding: 10px 20px;
  border-radius: 5px;
  font-size: 16px;
  cursor: pointer;
`;
export default function Index() {
  return <Button>Styled Components</Button>;
}

基于 props 动态添加样式

  • 通过给生成的组件添加 props 属性 ,来动态添加样式
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import React from 'react';
import styled from 'styled-components';

const Button = styled.button`
  background: #6a8bad;
  color: #fff;
  border: none;
  padding: 10px 20px;
  border-radius: 5px;
  font-size: 16px;
  cursor: pointer;
  /* 通过 props 来动态添加样式 */
  ${props => props.primary && `
    background: #fff;
    color: #6a8bad;
  `}
`;

export default function Index() {
  return <div>
    <Button>Styled Components</Button>
    <Button primary>Styled Components</Button>
  </div>;
}

继承样式

  • 可以通过继承方式来达到样式的复用
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import React from 'react';
import styled from 'styled-components';

const Button = styled.button`
  background: #6a8bad;
  color: #fff;
  border: none;
  padding: 10px 20px;
  border-radius: 5px;
  font-size: 16px;
  cursor: pointer;
`;

const PrimaryButton = styled(Button)`
  background: #fff;
  color: #6a8bad;
`;

export default function Index() {
  return <div>
    <Button>Styled Components</Button>
    <PrimaryButton>Styled Components</PrimaryButton>
  </div>;
}

# CSS IN JS 特点及注意事项

  • 特点
    • CSS IN JS 本质上放弃了 css ,变成了 css in line 形式,所以根本上解决了全局污染,样式混乱等问题
    • 运用起来灵活,可以运用 js 特性,更灵活地实现样式继承,动态添加样式等场景
    • 由于编译器对 js 模块化支持度更高,使得可以在项目中更快地找到 style.js 样式文件,以及快捷引入文件中的样式常量
    • 无须 webpack 额外配置 css,less 等文件类型
  • 注意事项
    • 虽然运用灵活,但是写样式不如 css 灵活,由于样式用 js 写,所以无法像 css 写样式那样可以支持语法高亮,样式自动补全等
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022/9/15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Hibernate框架学习之注解配置关系映射
     上篇文章我们通过注解对映射了单个实体类,但是具体项目中往往实体类之间又是相互关联的,本篇文章就是从实体类之间存在的不同关联角度,具体学习下如何映射他们之间的关联,主要涉及内容如下: 单向的一
Single
2018/01/04
2.3K0
Hibernate框架学习之注解配置关系映射
Hibernate学习笔记 多表映射
前面说了Hibernate的单表映射,由于是实体类和数据表之间一对一的映射,所以比较简单。现在就来说说多表映射,这需要涉及到多个实体类和数据表之间的关系。因此稍微复杂一点。
乐百川
2022/05/05
1.6K0
Hibernate基于外键映射的一对一关联关系
基于外键映射的一对一关联关系是Hibernate中常见的关系映射之一。在这种映射中,两个实体类之间存在一个一对一的关系,其中一个实体类作为主实体类,另一个实体类作为从实体类,并且从实体类中包含一个指向主实体类的外键。
堕落飞鸟
2023/05/15
8240
Hibernate双向一对多映射
双向一对多映射是Hibernate中常见的关系映射之一。在这种映射中,两个实体类之间存在一个一对多的关系,其中一个实体类作为“一”的一方,另一个实体类作为“多”的一方。
堕落飞鸟
2023/05/15
9160
JPA实体类中的注解
@Entity   标注于实体类上,通常和@Table是结合使用的,代表是该类是实体类 @Table   标注于实体类上,表示该类映射到数据库中的表,没有指定名称的话就表示与数据库中表名为该类的简单类名的表名相对应,如果是逆向生成表的话就会以简单类名作为表名   如果指定名称,例如@Table(name="tb_user"),就表示映射到数据库中的tb_userz这个表; @Id   标注于属性上,通常是在get方法上,也可以在属性的声明上。   用于表示该属性作为ID主键 @GeneratedValue
二十三年蝉
2018/03/29
3.9K0
Hibernate学习笔记 单表映射
配置好SessionFactory之后,我们就可以开始建立一对一的单表映射了。首先需要建立一个实体类,这里Getter、Setter、toString、equals等方法省略了。我们可以方便的使用IDEA或者Eclipse的生成代码功能轻松生成。
乐百川
2022/05/05
3620
Hibernate注解之基本注解的注解使用
文章目录 1. Hibernate注解之基本注解的注解使用 1.1. 使用注解须知 1.2. 类级别注解 1.3. 属性级别的注解 1.4. 主键相关的注解 1.5. 与非主键相关的注解 1.6. 实例 1.7. @JoinColumn 1.8. @JoinTabl 1.9. 参考文档 Hibernate注解之基本注解的注解使用 使用注解须知 我们在使用注解自动创建表的时候,系统会默认为我们创建一张表Hibernate_sequence,我们可以在Hibernate.cfg.xml文件中添加如下语句解决
爱撒谎的男孩
2019/12/31
2K0
[ SSH框架 ] Hibernate框架学习之四(JPA操作)
一、JPA概述以及它和Hibernate之间的关系 1.1.Hibernate 概述   JPA Java Persistence API,是EJB3规范中负责对象持久化的应用程序编程接口(ORM接口),它定义一系列的注释。这些注释大体可分为:类级别注释、方法级别注释、字段级别注释。给实体类添加适当的注释可以在程序运行时告诉Hibernate如何将一个实体类保存到数据库中以及如何将数据以对象的形式从数据库中读取出来。   目前有两种注释方案可以确定对象与表格之间的对应关系:一种是注释实体类的属性字段(字段级
Kevin_Zhang
2018/05/22
6.8K0
Hibernate 注解配置
近几年来,注解方式的配置因其简单易用的特点深受广大程序员的青睐,Hibernate也添加了对注解配置的支持。接下来我们就以论坛系统为例来讲解基于注解配置实体类和表的映射关系,以及实体和实体的关联关系。
用户9184480
2024/12/19
1040
Hibernate 注解配置
SSH框架之Hibernate第四篇
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
海仔
2019/09/18
3.6K0
Spring data 相关注解
作用是json序列化时将Java bean中的一些属性忽略掉,序列化和反序列化都受影响。
吐吐吐吐吐葡萄皮
2019/04/04
2.1K0
Spring data 相关注解
【Hibernate】Hibernate框架配置详解
通过Hibernate我们可以方便地操作数据库读取出来的信息,减少了繁琐的JDBC操作。
陈树义
2022/04/29
1.4K0
探索Hibernate的奥秘:简解其使用与实现过程
在现代的Java开发中,数据持久化是一个至关重要的环节。而在众多持久化框架中,Hibernate以其强大的功能和灵活性,成为了开发者们的首选工具。本文将详细介绍Hibernate的原理、实现过程以及其使用方法,希望能为广大开发者提供一些有价值的参考。
繁依Fanyi
2024/06/23
3320
Spring Data JPA 就是这么简单
jpa 的全称是 Java Persistence API , 中文的字面意思就是 java 的持久层 API , jpa 就是定义了一系列标准,让实体类和数据库中的表建立一个对应的关系,当我们在使用 java 操作实体类的时候能达到操作数据库中表的效果(不用写sql ,就可以达到效果),jpa 的实现思想即是 ORM (Object Relation Mapping),对象关系映射,用于在关系型数据库和业务实体对象之间作一个映射。
程序员小猿
2021/01/19
7K0
Spring Data JPA 就是这么简单
Java 数据库存储数组的方法
在现代软件开发中,数组是常用的数据结构之一。然而,在关系数据库中直接存储数组并不是一个简单的任务。关系数据库通常擅长存储简单的数据类型如整数、字符串和日期等,但对于复杂的数据类型如数组、列表或对象,通常需要采用特殊的方法进行处理。本文将详细介绍几种在Java中将数组存储到数据库的方法,包括使用JPA、JSON、XML、以及关系型数据库的数组类型等。
繁依Fanyi
2024/07/30
2810
【Java】已解决:org.hibernate.TypeMismatchException
已解决:org.hibernate.TypeMismatchException
屿小夏
2024/09/15
1370
【Java】已解决:org.hibernate.TypeMismatchException
Java 数据库存储数组的方法
在现代软件开发中,数组是常用的数据结构之一。然而,在关系数据库中直接存储数组并不是一个简单的任务。关系数据库通常擅长存储简单的数据类型如整数、字符串和日期等,但对于复杂的数据类型如数组、列表或对象,通常需要采用特殊的方法进行处理。本文将详细介绍几种在Java中将数组存储到数据库的方法,包括使用JPA、JSON、XML、以及关系型数据库的数组类型等。
繁依Fanyi
2024/09/20
1510
理解JPA注解@GeneratedValue
通过annotation来映射hibernate实体的,基于annotation的hibernate主键标识为@Id, 其生成规则由@GeneratedValue设定的.这里的@id和@GeneratedValue都是JPA的标准用法, JPA提供四种标准用法,由@GeneratedValue的源代码可以明显看出.
JavaEdge
2018/08/02
3.3K0
Spring Data JPA 时间注解:@CreatedDate 和 @LastModifiedDate
选择 Spring Data JPA 框架开发时,常用在实体和字段上的注解有@Entity、@Id、@Column等。在表设计规范中,通常建议保留的有两个字段,一个是更新时间,一个是创建时间。Spring Data JPA 提供了相应的时间注解,只需要两步配置,就可以帮助开发者快速实现这方面的功能。
happyJared
2020/02/11
4.4K1
Java一分钟之-JPA实体关系:一对一, 一对多, 多对多
Java Persistence API (JPA) 是Java平台上的一个对象关系映射 (ORM) 规范,用于简化数据库操作,其中实体关系的映射是核心内容之一。本文将深入浅出地探讨JPA中的三种基本实体关系类型:一对一、一对多、多对多,揭示常见问题、易错点及其避免策略,并附上简洁的代码示例。
Jimaks
2024/06/14
3860
相关推荐
Hibernate框架学习之注解配置关系映射
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验