聊聊React中CSS工程化技术方案

2,154 阅读4分钟

一、前言


本文的核心观点:适合自己的才是最好的!

本文将从css不同侧重点的类库及实现原理探索css工程化相关技术方案,比对其优劣点,旨在希望给各位在提升开发效率、制定css编码规范提供一些思路。

开始之前,我们先思考下下面三个问题:

1. 优秀CSS工程化技术方案的标准是什么?
2. CSS工程化要解决什么问题?
3. 不同的维度,你知道几种CSS工程化方案?
4. 你认为的最佳技术CSS工程化技术方案是什么?

好了,进入正题。下面文章围绕上面三个问题进行一一说明,我们开始吧!

二、CSS工程化标准是啥?


  1. 提升单人及协同开发效率。
  2. 方便扩展。
  3. 方便维护。
  4. 方便调试。
  5. 简单易用。

三、CSS工程化要解决什么问题


  1. 单人及协同开发效率问题。
  2. 业务扩展,需要样式表扩充以兼容需求。
  3. 新人加入,快速上手。老旧业务,方便维护。
  4. 方便调试,便于新人快速聚焦业务组件,梳理业务逻辑。
  5. 样式隔离:类名命名冲突时,由于css优先级会产生样式覆盖的问题。
  6. 简单易用易上手。

四、CSS工程化方案都有啥?


js三剑客的维度来讲,css工程化有3种方案,分别是:

    1. 以CSS预处理器Sass/Less + CSS后处理器Postcss为代表的纯CSS侧方案。
    1. TailWind.css为代表的以写辅助类为主的纯HTML侧方案。
    1. styled-componentsemotion 为代表的CSS-in-JS侧纯方案。它是针对React设计的一套css-in-js框架,当定义一个组件样式时,相当于创建了一个react组件。

1. 方案优劣分析

方案优点不足
CSS侧方案可以使用预处理语言的高级语法。结合BEM规范、css-modules可以解决样式隔离问题。BEM规范是一种约定,如果不遵守就会失控。css-modules通过给class类名增加hash戳以解决样式冲突,做到了js和css分离,不过这可能导致BEM规范下部分SCSS如@mixin的高级预发无法发挥它的作用。
HTML侧方案1.使用TailWind提供的辅助类结合style行内样式。 2.可以解决样式冲突问题1.多个class类名才能达到一个样式目的,不易维护。 2.行内样式优先级太高,不推荐大规模使用。Redium一个优秀的行内样式库。
CSS-in-JS侧方案官方推荐,能够优雅解决样式冲突问题1.js中写css,违背js css分离原则。2.对css预处理器不友好。3.不利于调试,不能看到原始类名。

工作中,我们往往是取各方精华,多种方案配合使用,即能解决样式隔离等痛点,又能满足开发效率需要。

2. 各方案实际效果

1)CSS-Modules

CSS Modules 并不是 CSS 官方的标准,也不是浏览器的特性,而是使用一些构建工具,比如 webpack,对 CSS 类名和选择器限定作用域的一种方式(类似命名空间)。原理是通过hash,使得类名唯一。

{
    loader: 'css-loader',
    options: {
        modules: true, // 开启模块化
        localIdentName: '[path][name]-[local]-[hash:base64:5]' // 类名名称
    }
}

image.png

localIdentName的参数:

  • [path] 表示样式文件相对于项目根目录所在的路径
  • [name] 表示样式文件名称
  • [local] 表示类名定义名称
  • [hash:length] 表示32位的hash值。注意:只有类名选择器和 ID 选择器才会被模块化控制,标签选择器是不会被模块化控制。
2)CSS-in-JS

image.png

image.png

3) Tailwind.css

image.png

image.png

五、我认为的CSS工程化方案最佳实践 - scoped style


4. scoped style

类似于 vue 的scoped,局部作用域样式。
原理:通过给dom增加唯一自定义属性,借助CSS属性选择器,实现样式表对应关系及样式隔离。
优点:

  1. html、css、js分离原则。
  2. 和scss完美配合,可以自由使用sass的高级语法为所欲为。

效果如下: image.png

使用方式:借助一个插件craco-plugin-scoped-css

yarn add craco-plugin-scoped-css

创建一个craco.config.js文件:

module.exports = {
  plugins: [
    {
      plugin: require('craco-plugin-scoped-css')
    }
  ]
}

再来个babel插件

yarn add babel-plugin-react-scoped-css --dev

.babelrc中增加

"plugins": ["babel-plugin-react-scoped-css"]

webpack loader

yarn add scoped-css-loader --dev
{
  test: /\.scss$/,
  use: [
    'style-loader',
    'css-loader',
    {
      loader: 'postcss-loader',
      options: {
        plugins: [require('tailwindcss'), require('autoprefixer')],
      },
    },
    { loader: 'scoped-css-loader' }, // 在这里
    {
      loader: 'sass-loader',
      options: {
        sourceMap: true,
        prependData: `
          @import "~ddo-assets/styles/mixin.scss";
          @import "~ddo-assets/styles/bemMixin.scss";
          @import "~ddo-assets/styles/_variables.scss";
        `
      },
    },
  ],
},

实现原理图示:

对于内联元素处理

github.com/FormidableL…

至于你喜欢哪一种,看你自己的心情~

七、相关优秀文档推荐