在 React 编程中, 我是如何用 tailwind 优化样式编写的?

2,780 阅读4分钟

最近在写自己的一个博客项目, 其中使用到了 tailwind 作为css框架, 关于 tailwind 的介绍可以参考我的另一篇文章:

2021年, 是时候来尝试 Tailwind 框架编写 CSS 了 https://juejin.cn/post/6935725712737304613

但是如何在我们的代码中更好的组织 tailwind 样式? 如何发挥它的优势, 避开它的劣势?

这就是我们这篇文章的主题。

对比几种编程方式

1. 传统 HTML 代码

传统开发中, style 单独为一个标签 或 其他引入外部样式表, 在 Html 标签中通过 class 属性引入对应类。

<head>
  <style>
  	.classA {
    	color: #000;
    }
  </style>
</head>
<body>
	<span class="classA">Hello World!</span>
</body>

不足:

  1. 重复利用相同的属性, 代码冗余大。
  2. 过于分散的类, 难以进行良好管理, 简单说起名字很头疼, 并且名不对意也是一种常见情况。
  3. 存在重名的情况, 没有命名空间。

2. CSS in JS

随着模块化思想的出现, 样式嵌套在组件模块中。

当然在 Reactstyle , 常常包含在组件的一个对象内进行编写, 这也是著名的 CSS in JS

我们通过组件 style 属性引入样式对象:

const style = {
  'color': 'red',
  'fontSize': '46px'
};

const clickHandler = () => alert('hi'); 

ReactDOM.render(
  <h1 style={style} onclick={clickHandler}>
     Hello, world!
  </h1>,
  document.getElementById('example')
);

现代主流打包工具的 CSS 已经支持 CSS Module, 我们通过引入外部样式也可达到模块样式的目的。

注意, 模块化CSS 返回类名映射作为键的对象

import style from './style.css'

const clickHandler = () => alert('hi'); 

ReactDOM.render(
  <h1 className={style.AclassName} onclick={clickHandler}>
     Hello, world!
  </h1>,
  document.getElementById('example')
);

优点:

  1. 模块化样式, 命名不会冲突, 存在各自的命名空间

    实际上通过给模块中的样式类名添加哈希后缀来避免重复。

  2. 代码组织相对良好

不足:

  1. 与传统编码类似, 同样的 CSS 代码仍需编写在各类中
  2. 代码冗余

3. 原子类: Tailwind 官方给出的案例

这是官方给出的 tailwind 重写上方实例

![]

这其实有点类似传统编写原子类, 不同的是这些功能类, 有一些是原子类的组合, 比如:

.bg-white {
  --tw-bg-opacity: 1;
  background-color: rgba(255, 255, 255, var(--tw-bg-opacity));
}

实际上, 原子类这种编写方式早期常被诟病

  1. 没有良好的代码组织, 太多类杂糅在一起, 难以进行调试
  2. 同样的样式虽然进行了简写, 但是仍需要在多个元素标签中编写, 存在冗余
  3. tailwind 本身的类优先级较低, 如果我们项目中用了其他的组件框架, 难以进行样式覆盖

我是怎么在 React 中进行 tailwind 样式编写的

  1. 首先我选择结合模块化的方式对代码进行组织, 不同的是, 这里我们用 className 进行样式引入:

    const classMap = {
    	chatNotification: "p-6 max-w-sm mx-auto bg-white rounded-xl shadow-md flex items-center space-x-4",
        chatNotificationLogoWrapper: "flex-shrink-0",
        chatNotificationLogo: "h-12 w-12"
        
    <div className={classMap.chatNotification}>
      <div className={classMap.chatNotificationLogoWrapper}>
        <img className={classMap.chatNotificationLogo} src="/img/logo.svg" alt="ChitChat Logo">
      </div>
    </div>
    

    这样一方面组织了代码样式, 另一方面提高了同类型元素样式的复用性。

  2. 使用模板字符串进行样式拓展

    如果说现有无法满足条件, 需要在 classMap 的基础上进行拓展, 我们可以使用模板字符串。

    举个栗子:

        <img class={`${classMap.chatNotificationLogo} h-14 w-14`} src="/img/logo.svg" alt="ChitChat Logo">
    

    这样来达到覆盖 tailwind 样式的目的。

  3. 覆盖组件框架的默认样式

    如果需要覆盖例如 antd 这类组件库的默认样式, 单凭原子类的优先级肯定是不够的, 我们就需要引入外部样式进行重写。

    这里可以利用 @apply 在外部样式表中编写 tailwind 文件:

    .menu li {
    	@apply text-base;
    }
    

    为了防止命名空间冲突, 请记得在打包工具中开启 CSS Module:

    import styles from './style.css'
    
    const classMap = {
    	menu: "flex flex-col justify-start items-center"
    }
    
    // 记住, 模块化CSS 是返回类名对象
    <Menu className={`${classMap.menu} ${styles.menu}`} />
    

    注意! 外部样式表中使用 @apply 只是将样式引入样式表, 仍会存在代码冗余

    所以如果可以, 还是请将样式写到 classMap 对象中, 在 className 中进行引用。

  4. 扩展 tailwind 样式

    其实这一部分就类似 第三点, 针对 hover, 动效 transform , tailwind 没办法精细化地生成类, 毕竟每个动画需求都不同。

    这时候就可以通过外部样式表来进行补充, 编写适配地样式。

总结

  1. 使用模块化保存类名列表, 进行代码组织
  2. 减少在外部样式中使用 @apply
  3. 使用模板字符串拼接类名列表。
  4. 通过外部引用样式表来进行覆盖 组件库样式, 提高优先级, 或扩展功能。

以上就是我对于在 react 中利用 tailwind 编写 css 代码的见解, 希望对你有帮助。

参考文章

CSS Module 阮一峰 www.ruanyifeng.com/blog/2017/0…

tailwind 官方文档 tailwindcss.com/docs/utilit…