中后台 CSS Modules 最佳实践

更新日期: 2022-05-24阅读: 963标签: css

工作中发现前端 css 的使用五花八门,有用 Sass,Less 这种预处理语言,还有 CSS in JS 这种奇葩玩法,还有 TailWindCSS 这种原子化的 CSS 方案,还有 CSS Modules 这种专注解决局部作用域和模块依赖问题的单纯技术。这么多种类,我们该怎么选呢,下面我介绍一种在现在微前端趋势下,在中后台项目中最好用的,开发体验最佳组合方式。

为什么要选择 CSS Modules

我们的这个最佳实践是以 CSS Modules 为基础的,为什么要选择他呢?在真实的工作中,我们遇到最痛的问题,就是样式的隔离,尤其是在微前端框架下,子应用之间,子应用和主应用之间,甚至同一个项目的不同页面之间都会有样式的覆盖,即使各种微前端框架都试图去解决样式隔离问题,不论是通过工程化加命名空间,还是 shadow dom 的方式,都无法一劳永逸的解决,都有其弊端,相比于 Less ,Sass 这个技术,都要在每个页面或者组件上人为的想一个命名空间,这个过程没有技术上的约束,单靠人之间的口头规范是没有用的,但 CSS Modules 无疑是一种彻底解决样式冲突问题的方法。

CSS Modules 的文档相当简单,10 分钟内就能学会,而且基本主流的工程化工具和脚手架都是支持的,比如 vite 默认支持,CRA 也是天然支持,不需要任何额外的配置。

CSS Modules 开发体验极佳,写 CSS 从未如此丝滑,后面会详细介绍。

CSS Modules + Less

CSS Modules 由于他非常的单纯,因此 module.css 文件,依然是遵循 CSS 文件的规范的,因此不能写嵌套。为了解决这个问题,我们引入 Less,也就是使用 module.less 的文件格式,这样我们就可以借助 Less 的能力,写嵌套的代码了。

为什么不用 Sass 呢?其实 Sass 和 Less 本质上没有太多区别,也没有什么好坏之分,我选择 Less 的原因是,我的项目中大量使用 antd 的组件库,而 antd 使用的是 Less 的方案,而且如果要定制 antd 的主题,就必须用 Less。

有了 Less 以后就可以有效的弥补,CSS Modules 的很多不足,尤其是嵌套,比如下面的代码。

.container {
  .header {
    color: red;
  }
}

变量的定义和使用

Less、CSS Modules 都支持变量的定义和使用,我们挨个看看是怎么用的:

// 定义 common.less 
@width: 10px;
@height: @width + 10px;

// 使用
@import './common.less';
.header {
  width: @width;
  height: @height;
}
// 定义 colors.css
@value blue: #0c77f8;
@value red: #ff0000;
@value green: #aaf200;

// 使用
@value colors: "./colors.css";
@value blue, red, green from colors;
.title {
  color: red;
  background-color: blue;
}

这两种方式在定义和使用上,都比较麻烦,尤其是在使用的时候,需要显式的导入,而我推荐的是另一种方式:就是 CSS 原生支持的方式。使用文档查看:MDN CSS Variables 基本使用方式如下:

// 定义全局变量
:root {
  --main-color: #fff;
}

// 定义局部变量
.container {
  --main-color: #000;
}

// 使用变量
.component {
  color: var(--main-color);
}

我们可以看到,变量有明确的 -- 前缀,比较容易区分,而且使用方便不需要导入,而且很容易做覆盖。如果我们看最新版本的 antd-mobile 的组件库中,就大量使用这种原生的方式做主题的定制和样式的覆盖。

至于兼容性这块,在中后台场景下,Chrome 的支持是非常好的,基本不需要考虑。

Class 的复用

在 Less 中有基于 extend 和 Mixins 的继承方式,但我觉得都没有 CSS Modules 的继承方式更方便,尤其是 Mixins 这种反常识的使用方式,一旦写不好代码就很容易散、并且不便于维护、新手难以理解。使用 CSS Modules 的 composes 的方式如下:

// 定义
.container {
  color: #fff;
}
// 相同文件下调用
.component {
  composes: container;
}

// 不同文件下调用
.component {
  composes: container from './index.module.less';
  color: #000;
}

如上述的代码,最终会被编译成 <div/>   且最终生效的 color 是 #000。

如何覆盖第三方组件样式?

我们在平时的编码中经常会去覆盖第三方组件的样式,比如我们使用了 antd 中 Button 的样式,在 module.less 中,我们可以使用 :global   关键字,只要使用他的地方都不会在编译时自动添加 Hash,而且这种方式下,也可以给他设定唯一的父元素的 class ,这样你改变的第三方组件的样式就不会影响别的也同样引用该组件的地方的样式。

.container {
  :global(.ant-button) {
    color: var(--main-color);
  }
}

计算样式 classnames

如果一个组件的 class 可能需要多个,或者有可能需要一定的计算,传统的 CSS Modules 的使用方式是比较丑陋的,因此我们使用一种更为优雅的方式来解决,就是借助第三方 npm 包,classnames 的能力。如下:

// 当 className 需要多个 class 的时候,我们直接使用 classnames 传多个参数的方式
<div className={classnames(style.container1, style.container2)} />
// 最终会编译成 <div class="_contianer1_i323u _container2_i889k" />

// 如果某个 class 是需要一定的逻辑判断的,可以把一个对象传入,用 value 的 false 或者 true 
// 来控制 class 的有无
<div className={classnames({ [style.container1]: true, [style.container2]: false })} />

// 这种方式,是上面两种方式的组合,classnames 可以接收多参数,对象,甚至是数组
<div className={classnames('body', {[style.container1]: true, [style.container2]: false })} />

让人欲罢不能的开发体验

传统写 css 是很难通过编辑器在 JSX  的 div className 上,按住 cmd + 点击快速显示或者定位到样式代码的,但如果我们使用了 CSS Modules ,并且在安装了 VSCode CSS Modules 扩展以后。

如下图所示:我们就可以轻松实现定位和显示,甚至不需要切换到 Less 文件里。


当时真正使用的时候就知道有多爽了。

当然,使用 CSS Modules 还有一个巨大且显而易见的好处是,我们不需要纠结 class 的命名,不同组件内我们甚至可以定义相同的名字,比如:

import style from './index.module.less';
const Login = () => (
  <div className={style.container}>
    <div className={style.header}>登录</div>
  </div>);
    
const Register = () => (
  <div className={style.container}>
    <div className={style.header}>注册</div>
  </div>);

我们看到,Login 和 Register 组件,我们都使用了 container 和 header 两个 class ,而不需要在前面加组件的前缀。这样更有利于代码的复用,而且可以很好的表达页面的结构。

如果是写 NPM 组件怎么办?

CSS Modules 用在项目的业务代码里是没有问题的,但如果我们想把一些组件做成 NPM 包给别人使用,如果我们用了 CSS Modules ,编译后的 NPM 包,也会把 class 上都加上 Hash 的,是动态变化的。因此当别人想覆盖你的样式的时候,就非常困难了。这个问题怎么解决呢?

确实,社区给出了一些答案,可以看看下面的文档:customizing-components

这里面提出了两个观点,一个是妄图去覆盖别人组件的样式,这本身就是一种 Hack 的行为,我们应该使用更优雅的方式实现,应该让 NPM 组件提供对应的 api 让外部调用修改,第二就是社区提供了一个工具包,react-css-themr,每个 NPM组件接受外部传 theme 参数(css module 对象),用来定义所有样式。示例如下:

import React from 'react';
import { AppBar } from 'react-toolbox/lib/app_bar';
import theme from './PurpleAppBar.css';

const PurpleAppBar = (props) => (
  <AppBar {...props} theme={theme} />
);

export default PurpleAppBar;

上述最佳实践经过本人的多年验证,真实有效,童叟无欺,如果大家喜欢或者不喜欢都可以尝试用起来,早用早享受,晚用晚开心。

来源:https://mp.weixin.qq.com/s/qUiiUkKOyW-QnBz1WEOTwQ

链接: https://www.fly63.com/article/detial/11578

css自动省略号...,通过css实现单行、多行文本溢出显示省略号

网页开发过程中经常会遇到需要把多行文字溢出显示省略号,这篇文章将总结通过多种方法实现文本末尾省略号显示。

深入理解letter-spacing,word-spacing的对比区别

word-spacing 属性增加或减少单词间的空白(即字间隔)。 letter-spacing 属性增加或减少字符间的空白(字符间距)。

整理一下CSS最容易躺枪的二十规则,大家能躺中几条?

满屏div之css最容易中枪的二十条规则,你中枪了吗?一、float:left/right 或者 position: absolute 后还写上 display:block?二、认为布局就是 Float,所有的地方都是 Float,全家都是 Float!

如何编写轻量级 CSS 框架

我想每个人都应该归纳总结工作中的常见需求,编写一套适合自己的 CSS 框架。大多数的轻量级框架只是 CSS 框架,不涉及 JS 部分,主要用于网页的布局。

精简CSS代码,提高代码的可读性和加载速度

定义简洁的CSS规则:CSS的每条规则中都包含了规则的属性及属性值。定义简洁的CSS规则主要是指合并相关规则和定义简洁的属性值。

css完美解决网页在iphoneX的头部刘海显示问题

css完美解决iphonX白条,网站扩展到整个屏幕,CSS Shapes中有个CSS属性名为shape-outside实现元素滚动自动环绕iPhone X刘海

css中 出现height为100%失效的原因及解决方案

我们都知道需要给html和body标签设置了高度height:100%之后,再给内部的div设置height:100%的时候,内部div的高度100%才会起到作用。这是由于:%是一个相对父元素计算得来的高度,要想使他有效,我们需要设置父元素的height。

CSS3 clip-path 用法介绍

clip-path属性可以创建一个只有元素的部分区域可以显示的剪切区域。区域内的部分显示,区域外的隐藏。剪切区域是被引用内嵌的URL定义的路径或者外部svg的路径,或者作为一个形状例如circle().。clip-path属性代替了现在已经弃用的剪切 clip属性。

CSS中可以和不可以继承的属性

这篇文章整理css中无继承性的属性、继承性的属性、所有元素可以继承的属性、内联元素可以继承的属性、块级元素可以继承的属性

你知道我们平时在CSS中写的%都是相对于谁吗?

编写CSS的时候,经常会用到百分比赋值(%)实现自适应。像我们最常使用的流式布局设计模式,基本所有的column的宽度都是通过%来取值的。或者比如经常会遇到的元素水平垂直居中问题

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!