50万奖金+官方证书,深圳国际金融科技大赛正式启动,点击报名 了解详情
写点什么

如何在 React 中优雅的写 CSS

  • 2021-03-08
  • 本文字数:3897 字

    阅读完需:约 13 分钟

如何在 React 中优雅的写 CSS

引言


问题:CSS 文件分离 !=  CSS 作用域隔离


看下这样的目录结构:

├── src                                  │   ├──......                   # 公共组件目录│   ├── components              # 组件│   │   └──comA                 # 组件A│   │       ├──comA.js                     │   │       ├──comA.css                      │   │       └── index.js                  │   │   └──comB                 # 组件B│   │       ├──comB.js                     │   │       ├──comB.css                      │   │       └── index.js                  │   ├── routes                  # 页面模块                  │   │   └── modulesA            # 模块A│   │       ├──pageA.js         # pageA JS 代码│   │       ├──pageA.css        # pageA CSS 代码
复制代码

看目录结构清晰明了,由于“ CSS 文件分离 !=  CSS 作用域隔离”这样的机制,如果我们不通过一些工具或规范来解决 CSS 的作用域污染问题,会产生非预期的页面样式渲染结果。


假设我们在组件 A 和组件 B  import 引入 comA.css 和 comB.css。

comA.css

.title {    color: red;}
复制代码

comB.css

.title {    font-size: 14px;}
复制代码

最后打包出来的结果为:

.title {    color: red;}.title {    font-size: 14px;}
复制代码


我们希望,comA.css 两者互不影响,可以发现,虽然 A、B 两个组件分别只引用了自己的 CSS 文件,但是 CSS 并没有隔离,两个 CSS 文件是相互影响的!


随着 SPA 的流行,JS 可以组件化,按需加载(路由按需加载、组件的 CSS 和 JS 都按需加载),这种情况下 CSS 作用域污染的问题被放大,CSS 被按需加载后由于 CSS 全局污染的问题,在加载出其他一部分代码后,可能导致现有的页面上会出现诡异的样式变动。这样的问题加大了发布的风险以及 debugger 的成本。


小编我从写 Vue 到写 React , Vue 的 scoped 完美的解决了 CSS 的作用域问题,那么 React 如何解决 CSS 的作用域问题呢?


解决 React 的 CSS 作用域污染方案:

  • 方案一:namespaces

  • 方案二:CSS in JS

  • 方案三:CSS Modules


方案一:namespaces


利用约定好的命名来隔离 CSS 的作用域


comA.css

.comA.title {    color: red;}.comA .……{    ……}
复制代码

comB.css

.comB.title {    font-size: 14px;}.comB .……{    ……}
复制代码

嗯,用 CSS 写命名空间写起来貌似有点累。

没事我们有 CSS 预处理器,利用 less、sass、stylus 等预处理器,代码依然简洁。


A.less

.comA {    .title {        color: red;    }        .…… {        ……    }}
复制代码

B.less

.comB {    .title {        font-size: 14px;    }        .…… {        ……    }}
复制代码

貌似很完美解决了 CSS 的作用域问题,但是问题来了,假设 AB 组件是嵌套组件。


那么最后的渲染 DOM 结构为:

<div class="comA">    <h1 class="title">组件A的title</h1>    <div class="comB">        <h1 class="title">组件组件的title</h1>    </div></div>
复制代码

comA 的样式又成功作用在了组件 B 上。


没关系,还有解,所有的 class 名以命名空间为前缀。

<div class="comA">    <h1 class="comA__title">组件A的title</h1>    <div class="comB">        <h1 class="comB__title">组件组件的title</h1>    </div></div>
复制代码


A.less

.comA {    &__title {        color: red;    }}
复制代码

B.less

.comB {    &__title {        font-size: 14px;    }}
复制代码

如果,我们的样式还遵循 BEM (Block, Element, Modifier) 规范,那么,样式名简直不要太长!但是问题确实也解决了,但约定毕竟是约定,靠约定和自觉来解决问题毕竟不是好方法,在多人维护的业务代码中这种约定来解决 CSS  污染问题也变得很难。


方案二:CSS in JS


使用 JS 语言写 CSS,也是 React 官方有推荐的一种方式。


从 React 文档进入 https://github.com/MicheleBertoli/css-in-js ,可以发现目前的 CSS in JS 的第三方库有 60 余种。


看两个比较大众的库:

  • reactCSS

  • styled-components

reactCSS


支持 React、Redux、React Native、autoprefixed、Hover、伪元素和媒体查询(http://reactcss.com/)


看下官网文档 :

const styles = reactCSS({  'default': {    card: {      background: '#fff',      boxShadow: '0 2px 4px rgba(0,0,0,.15)',    },  },  'zIndex-2': {    card: {      boxShadow: '0 4px 8px rgba(0,0,0,.15)',    },  },}, {  'zIndex-2': props.zIndex === 2,})
复制代码


class Component extends React.Component {  render() {    const styles = reactCSS({      'default': {        card: {          background: '#fff',          boxShadow: '0 2px 4px rgba(0,0,0,.15)',        },        title: {          fontSize: '2.8rem',          color: this.props.color,        },      },    })    return (      <div style={ styles.card }>        <div style={ styles.title }>          { this.props.title }        </div>        { this.props.children }      </div>    )  }}
复制代码

可以看出,CSS 都转化成了 JS 的写法,虽然没有学习成本,但是这种转变还是有一丝不适。


styled-components


styled-components,目前社区里最受欢迎的一款 CSS in JS 方案(https://www.styled-components.com/)


const Button = styled.a`  /* This renders the buttons above... Edit me! */  display: inline-block;  border-radius: 3px;  padding: 0.5rem 0;  margin: 0.5rem 1rem;  width: 11rem;  background: transparent;  color: white;  border: 2px solid white;  /* The GitHub button is a primary button   * edit this to target it specifically! */  ${props => props.primary && css`    background: white;    color: palevioletred;  `}`render(  <div>    <Button      href="https://github.com/styled-components/styled-components"      target="_blank"      rel="noopener"      primary    >      GitHub    </Button>    <Button as={Link} href="/docs" prefetch>      Documentation    </Button>  </div>)
复制代码


方案三:CSS Modules


利用 webpack 等构建工具使 class 作用域为局部。


CSS 依然是还是 CSS,例如 webpack,配置 css-loader 的 options modules: true。

module.exports = {  module: {    rules: [      {        test: /\.css$/,        loader: 'css-loader',        options: {          modules: true,        },      },    ],  },};
复制代码

modules 更具体的配置项参考:https://www.npmjs.com/package/css-loader

loader 会用唯一的标识符 (identifier) 来替换局部选择器。所选择的唯一标识符以模块形式暴露出去。


示例: webpack css-loader options

options: {  ...,  modules: {    mode: 'local',    // 样式名规则配置    localIdentName: '[name]__[local]--[hash:base64:5]',  },},...
复制代码

App.js

...import styles from"./App.css";...<div>  <header className={styles["header__wrapper"]}>    <h1 className={styles["title"]}>标题</h1>    <div className={styles["sub-title"]}>描述</div>  </header></div>
复制代码

App.css

.header__wrapper {  text-align: center;}
.title { color: gray; font-size: 34px; font-weight: bold;}
.sub-title { color: green; font-size: 16px;}
复制代码

编译后端的 CSS,classname 增加了 hash 值。

.App__header__wrapper--TW7BP {  text-align: center;}
.App__title--2qYnk {  color: gray;  font-size: 34px;  font-weight: bold;}
.App__sub-title--3k88A {  color: green;  font-size: 16px;}
复制代码


总结


(1)如果是 ui 组件库中使用


建议使用 namespaces 方案


原因:

  • ui 组件库维护人员基本固定,遵守约定的规范较为容易,可通过约定规范来解决不同组件 CSS 相互影响问题

  • 由于 ui 组件库会应用于整个公司的产品,在真正的业务场景中,虽然不建议,但是可能无法避免需要覆盖组件样式的特殊场景,如使用其他两种方式,不能支持组件样式覆盖


(2)如果是业务代码/业务组件中使用


CSS in JS  / CSS Modules


业务代码维护人员较多且不固定、代码水平不一致,只通过规范来约束不靠谱,无法保证开发人员严格遵守规范,不能根治 CSS 交叉影响问题,但是从 debug 角度考虑,建议组件外层都添加一个 namespaces 方面定位组件。然后加之 CSS in JS 或 CSS Modules 方案来解决 CSS 交叉影响问题。


CSS in JS 和 CSS Modules 谁优谁胜?


CSS Modules 会比 CSS in JS 的侵入性更小,CSS in JS 可以和 JS 共享变量,但个人更喜欢 CSS Modules ,但是谁优谁胜无法武断。


  • 如果你的团队还没有使用这任一技术,需要考虑的是团队成员的感受

  • 如果已经在使用其中某一种方案,保持一致性即可,相信并这样走下去



头图:Unsplash

作者:七喜

原文:https://mp.weixin.qq.com/s/c0zbwrqDQOAhwZulNV4Dtw

原文:如何在 React 中优雅的写 CSS

来源:政采云前端团队 - 微信公众号 [ID:Zoo-Team]

转载:著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

2021-03-08 23:525185

评论 2 条评论

发布
用户头像
如果你更喜欢vue的scope方案也可以在react里用——scoped-css-loader
2021-03-12 17:37
回复
用户头像
目前我们微前端项目也遇到了这种问题,我负责的项目是采用的namespace,另一个同学的项目用的 CSS Modules,一次规范对齐会上也谈了这件事是否要保持一致,我们都各有考虑,最后 Leader 的结论也是保持自己项目内风格一致就行
2021-03-09 10:42
回复
没有更多了
发现更多内容

蔚来汽车 x TiDB丨单表超 20 亿条数据,从 MySQL 到 TiDB 的迁移思考与实践

PingCAP

MySQL TiDB 蔚来

基于人工智能的代码分析与 Bug 检测实战

霍格沃兹测试开发学社

面试官:说说你的项目亮点?

王磊

Java

会声会影发光字体制作 会声会影字体怎么淡化退出 视频剪辑制作教程

阿拉灯神丁

字幕 会声会影2023 视频剪辑软件下载 视频剪辑软件

如何判断IP地址属于住宅IP还是机房IP

IPIDEA全球HTTP

代理IP

跨平台数据同步:京东商品详情API的多平台支持

技术冰糖葫芦

api 货币化 API 文档 API 测试 pinduoduo API

AI大模型在业务受理的智能化实践和探索

鲸品堂

运营商 大模型 企业服务大模型

JavaScript 中的闭包和事件委托

不在线第一只蜗牛

JavaScript 前端

OpenTiny HUICharts 正式开源发布,一个简单、易上手的图表组件库

OpenTiny社区

Vue js eCharts OpenTiny

【Java】Jsoup 解析HTML报告

不在线第一只蜗牛

Java html

TCL 实业 x TiDB丨从分销转向零售,如何考虑中台建设和数据库选型?

PingCAP

TCL 营销中台 实业

3个办公网站,助你又快又好地搞定工作总结ppt!

彭宏豪95

效率工具 职场 PPT 效率办公 AI生成PPT

淘宝天猫商品详情API:商品参数对比与选择

技术冰糖葫芦

API api 货币化 API 文档 API 测试

软件测试学习笔记丨BlueOcean 安装与使用

测试人

软件测试

电车风噪大?巧妙利用空气动力学和仿真技术,解决风噪影响

Altair RapidMiner

汽车 仿真 汽车仿真 altair 新能源车

三星Galaxy Z Flip6:内外兼修小折花魁,解锁科技改变生活新方式

科技热闻

Web3 游戏周报(7.28 - 8.03)

Footprint Analytics

链游

2025第十三届中国电子信息博览会(CITE深圳电子展)

AIOTE智博会

电子展 深圳电子展 电子信息展

聊聊JVM如何优化

京东科技开发者

沧州杨埕水库:创建巡检“二维码”,为安全管理赋能

草料二维码

安全管理 设备管理 草料二维码 设备巡检二维码 设备巡检系统

ETL数据集成丨将GreenPlum数据同步至Doris数仓

谷云科技RestCloud

Doris greenplum 数据同步 ETL 数据集成工具

微软将深度整合 Azure AI 与 GitHub;Stability AI 0.5 秒生成三维建模丨 RTE 开发者日报

声网

【京东云新品发布月刊】2024年7月产品动态

京东科技开发者

JAVA应用CPU跳点自动DUMP工具

京东科技开发者

腾讯云联合中科软发布 “保险行业一体化大数据解决方案” 共筑保司数据基座

腾讯云大数据

腾讯云 wedata

TiKV Raft 快照全流程丨TiKV 源码解读(二十二)

PingCAP

数据库 TiKV 源码解读 TiKV

如何在 React 中优雅的写 CSS_语言 & 开发_政采云前端团队_InfoQ精选文章