侯体宗的博客
  • 首页
  • 人生(杂谈)
  • 技术
  • 关于我
  • 更多分类
    • 文件下载
    • 文字修仙
    • 中国象棋ai
    • 群聊
    • 九宫格抽奖
    • 拼图
    • 消消乐
    • 相册

如何在 React 中优雅的写 CSS?

前端  /  管理员 发布于 6年前   425

本文首发于政采云前端团队博客:如何在 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余种。

看两个比较大众的库:

reactCSSstyled-componentsreactCSS
支持 React、Redux、React Native、autoprefixed、Hover、伪元素和媒体查询

看下官网文档 :

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-componentsstyled-components,目前社区里最受欢迎的一款 CSS in JS 方案
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="http://www.fly63.com/docs" prefetch>      Documentation    </Button>  </div>)

与 reactCSS 不同,styled-components 使用了模板字符串,写法更接近 CSS 的写法。


方案三: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 ,但是谁优谁胜无法武断。

如果你的团队还没有使用这任一技术,需要考虑的是团队成员的感受如果已经在使用其中某一种方案,保持一致性即可,相信并这样走下去



  • 上一条:
    解决vue动态绑定audio/video的src不能播放
    下一条:
    vue 无限滚动问题
  • 昵称:

    邮箱:

    0条评论 (评论内容有缓存机制,请悉知!)
    最新最热
    • 分类目录
    • 人生(杂谈)
    • 技术
    • linux
    • Java
    • php
    • 框架(架构)
    • 前端
    • ThinkPHP
    • 数据库
    • 微信(小程序)
    • Laravel
    • Redis
    • Docker
    • Go
    • swoole
    • Windows
    • Python
    • 苹果(mac/ios)
    • 相关文章
    • 使用 Alpine.js 排序插件对元素进行排序(0个评论)
    • 在js中使用jszip + file-saver实现批量下载OSS文件功能示例(0个评论)
    • 在vue中实现父页面按钮显示子组件中的el-dialog效果(0个评论)
    • 使用mock-server实现模拟接口对接流程步骤(0个评论)
    • vue项目打包程序实现把项目打包成一个exe可执行程序(0个评论)
    • 近期文章
    • 在windows10中升级go版本至1.24后LiteIDE的Ctrl+左击无法跳转问题解决方案(0个评论)
    • 智能合约Solidity学习CryptoZombie第四课:僵尸作战系统(0个评论)
    • 智能合约Solidity学习CryptoZombie第三课:组建僵尸军队(高级Solidity理论)(0个评论)
    • 智能合约Solidity学习CryptoZombie第二课:让你的僵尸猎食(0个评论)
    • 智能合约Solidity学习CryptoZombie第一课:生成一只你的僵尸(0个评论)
    • 在go中实现一个常用的先进先出的缓存淘汰算法示例代码(0个评论)
    • 在go+gin中使用"github.com/skip2/go-qrcode"实现url转二维码功能(0个评论)
    • 在go语言中使用api.geonames.org接口实现根据国际邮政编码获取地址信息功能(1个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf分页文件功能(95个评论)
    • gmail发邮件报错:534 5.7.9 Application-specific password required...解决方案(0个评论)
    • 近期评论
    • 122 在

      学历:一种延缓就业设计,生活需求下的权衡之选中评论 工作几年后,报名考研了,到现在还没认真学习备考,迷茫中。作为一名北漂互联网打工人..
    • 123 在

      Clash for Windows作者删库跑路了,github已404中评论 按理说只要你在国内,所有的流量进出都在监控范围内,不管你怎么隐藏也没用,想搞你分..
    • 原梓番博客 在

      在Laravel框架中使用模型Model分表最简单的方法中评论 好久好久都没看友情链接申请了,今天刚看,已经添加。..
    • 博主 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 @1111老铁这个不行了,可以看看近期评论的其他文章..
    • 1111 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 网站不能打开,博主百忙中能否发个APP下载链接,佛跳墙或极光..
    • 2016-10
    • 2016-11
    • 2017-06
    • 2017-07
    • 2017-08
    • 2017-09
    • 2017-10
    • 2017-11
    • 2018-03
    • 2018-04
    • 2018-05
    • 2018-06
    • 2018-09
    • 2018-11
    • 2018-12
    • 2019-02
    • 2020-03
    • 2020-04
    • 2020-05
    • 2020-06
    • 2021-04
    • 2021-05
    • 2021-07
    • 2021-08
    • 2021-09
    • 2021-10
    • 2021-11
    • 2022-08
    • 2022-09
    • 2022-10
    • 2022-11
    • 2022-12
    • 2023-01
    • 2023-02
    • 2023-03
    • 2023-04
    • 2023-05
    • 2023-06
    • 2023-07
    • 2023-09
    • 2023-10
    • 2023-11
    • 2023-12
    • 2024-01
    • 2024-02
    • 2024-03
    • 2024-04
    Top

    Copyright·© 2019 侯体宗版权所有· 粤ICP备20027696号 PHP交流群

    侯体宗的博客