两种方式轻松做react css样式隔离

3,662 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第2天,点击查看活动详情

引言

在书写react组件时,如果写了相同的样式类名,很容易造成样式跨组件污染

在vue.js框架中做css样式隔离可以直接在style标签上加scoped
它的原理是通过 Postcss 工具来实现的,Postcss会对vue组件样式做代码转换,达到样式唯一效果

问题

但在react中,官方并没有给我们做样式隔离处理,虽然我们可以借助一些css库来达到样式隔离效果,比如styled-components,但自己如何能实现呢

本文介绍两种方式

  1. 使用 namespaces 空间命名来隔离CSS
  2. 使用 css module 来隔离CSS

namespaces 空间命名

基本: 最基本的写法就是直接统一在类名前加上固定前缀(前缀可以是各自组件名称)

// title即为该组件的唯一前缀
function Title() {
  return (
    <div className='title'>
      <div className='title-name'></div>
      <div className='title-time'></div>
      <div className='title-content'>
        <div className='title-content-item'></div>
        <div className='title-content-item'></div>
        <div className='title-content-item'></div>
      </div>
    </div>
  )
}
/**
 * 相应的样式可以使用 & 符代替前缀(scss)
 */
.title{
  &-name{
  }
  &-time{
  }
  &-content{
    &-item{
    }
  }
}

提取前缀写法(进阶): 使用工具函数提取(前缀可以是组件名称)

import style from './title.less';
import extractPrefix from 'common/utils/extractPrefix'

// ex函数会给样式类名自动加上前缀
const ex = extractPrefix(style)

function Title() {
  return (
    <div className={ex('title')}>
      <div className={ex('name')}></div>
      <div className={ex('time')}></div>
      <div className={ex('content')}>
      <div className={ex('content-item')}></div>
      <div className={ex('content-item')}></div>
      <div className={ex('content-item')}></div>
      </div>
    </div>
  )
}
/**
 * 相应的less
 * 自定义样式前缀prefix
 */
@prefix: title; // 前缀(该组件名,每个组件可以使用自己的名称)

:export {
  prefix: @prefix;
}

.@{prefix}{
  &-name{
  }
  &-time{
  }
  &-content{
    &-item{
    }
  }
}

工具函数extractPrefix:

import classnames from 'classnames';

export default (styles, _prefix) => {
  const prefix = _prefix || styles.prefix; // 获取自定义前缀
  const cx = classnames.bind(styles); // classnames为一个拼接类名的简单js库
  
  if (!prefix) return cx; //无需函数拼接
  
  // 前缀处理
  const handlePrefix = name => {
    if (name.startsWith(':')) return name.replace(':', '');
    return `${prefix}-${name}`
  };
  
  // 遍历传入的类名,使用cx生成类名
  // cx参数可以传入字符串、对象等格式,本文只使用字符串案例,详细可上npm查看classnames库
  return (...names) =>
    cx(
      names.map(name => {
        if (name === null || name === '' || name === undefined) return ''
        
        // 接受字符串
        if (typeof name === 'string') {
          return handlePrefix(name)
        }
        
        // 接受对象
        if (typeof name === 'object') {
          const returnObj = {};
          Object.keys(name).forEach(key => {
            const element = name[key];
            returnObj[handlePrefix(element)] = element
          })
          return returnObj;
        }
        return ''
      })
    );
};

CSS Modules

CSS Modules 是什么?
根据CSS Modules项目在github上的翻译:
CSS 模块是一个CSS 文件,默认情况下,所有类名和动画名都在本地范围内

意思就是说css modules它不是将 CSS 改造成编程语言,而是功能很单纯,只加入了局部作用域和模块依赖,这恰恰是网页组件最急需的功能,因此,CSS Modules 很容易学,因为它的规则少,同时又非常有用,可以保证某个组件的样式,不会影响到其他组件

CSS Modules在react中的用法
此处以sass为例
第一步将scss文件中间加入.module,代表为CSS Modules文件

WechatIMG168.png
page1的样式为:.title{ color: red; }
page2的样式为:.title{ color: blue; }

第二步引入module文件,以对象的形式使用

import style from './style.module.scss'

function Page1() {
  return (
    <div>
      <div className={style.title}>攀枝花大芒果,便宜好吃包邮</div>
    </div>
  );
}

export default Page1;

page2同page1一样
最终展示效果:同为.title样式,但不会出现覆盖情况

WX20220604-222711@2x.png

总结

css样式隔离的方法各有千秋,适合自己的才是最好的!
坚持学习!耀眼一点,你有资格!

往期精彩文章

🌟彻底理解redux的中间件原理
🌟canvas实现刮刮奖效果
🌟前端实现pdf下载
🌟web前端性能优化(全汇总)
🌟一句话概括this指向问题
🌟MutationObserver 实现微任务原理分析
🌟遇到几次的大厂笔试题:装饰数组push方法
🌟V8垃圾回收策略与GC算法
🌟浏览器缓存策略(强缓存和协商缓存)
🌟$nextTick 源码解读与原理分析
🌟手动封装适合react hook使用的状态管理工具