AI实践哪家强?来 AICon, 解锁技术前沿,探寻产业新机! 了解详情
写点什么

如何在 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:524958

评论 2 条评论

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

网络空间测绘系统的商业应用

郑州埃文科技

网络空间测绘技术

vivo 消息中间件测试环境项目多版本实践

vivo互联网技术

RocketMQ 消息中间件 多版本环境隔离

ChatGPT与传统搜索引擎的区别:智能对话与关键词匹配的差异

天津汇柏科技有限公司

搜索引擎; ChatGPT

【论文速读】| 越狱:大语言模型安全训练是如何失败的?

云起无垠

火山引擎发布ByteHouse性能白皮书,揭秘OLAP性能突破的关键技术

字节跳动数据平台

数据库 大数据 云原生 数仓 企业号 3 月 PK 榜

发掘非结构化数据价值:AI 在文档理解领域的现状与未来

Baihai IDP

程序员 AI 非结构化数据 白海科技

IO模型介绍(select、poll、epoll)

京东科技开发者

独立站如何做好移动端的优化处理呢?

技术冰糖葫芦

API 接口

探索华为云CCE敏捷版金融级高可用方案实践案例

华为云开发者联盟

开发 华为云 容器集群 华为云开发者联盟 华为云CCE

"线程池中线程异常后:销毁还是复用?"

京东科技开发者

百度智能云千帆,产业创新新引擎

百度Geek说

AI 百度智能云 千帆大模型平台

solana链土狗抢单机器人

区块链技术

java线程池原理浅析

京东科技开发者

AI能代替软件工程师吗?

小魏写代码

京东商品列表数据接口

tbapi

京东 京东API接口 京东商品列表数据接口

助力水下潜行:浮力调节系统仿真

DevOps和数字孪生

XPath攻略:从入门到精通,元素查找不再难

霍格沃兹测试开发学社

抢开盘、抢土狗、批量抢买、卖出机器人工具使用说明

区块链技术

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

京东科技开发者

Python数据库编程全指南SQLite和MySQL实践

华为云开发者联盟

Python MySQL 开发 华为云 华为云开发者联盟

大型IM工程重构实践:企业微信Android端的重构之路

JackJiang

即时通讯;IM;网络编程

XPath攻略:从入门到精通,告别查找困难!

测试人

软件测试

2024南京国际自动驾驶技术展览会

AIOTE智博会

自动驾驶展 智能驾驶展

咸鱼翻身啦!咸鱼之王完美内购版架设教程

echeverra

咸鱼之王

展示大屏-24小时天气预报

叫练

大模型时代下的“金融业生物识别安全挑战”机遇

中关村科金

安全 金融 防伪 生物识别

火山引擎VeDI:A/B测试开放平台正式上线,企业个性化平台一键定制

字节跳动数据平台

大数据 A/B测试 企业号 3 月 PK 榜 对比试验 数字化增长

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