Redian新闻
>
中后台 CSS Modules 最佳实践

中后台 CSS Modules 最佳实践

科技


工作中发现前端 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 {
  colorvar(--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 class="_container_i32us _component_iw22a"/> 且最终生效的 color 是 #000。


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



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

.container {
  :global(.ant-button) {
    colorvar(--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 {...propstheme={theme} />
);

export default PurpleAppBar;



总结



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




关注「Alibaba F2E」微信公众号把握阿里巴巴前端新动向



微信扫码关注该文公众号作者

戳这里提交新闻线索和高质量文章给我们。
相关阅读
宗教品牌lululemon创始人的“朝圣”人生:父母离异、屡次破产、连续创业15年惊!加拿大著名运动品牌lululemon涉 "以次充好" 被北京西城市监部门罚款8.1万元!关于 Product-Led Onboarding 的认知和实践AmEx Delta SkyMiles Reserve Business 商业信用卡110k 开卡奖励;退役波音747限量版卡面Bitter Lessons From a Chinese Education ReformerTangshan Police Officer Dismissed After Female Diners Assaulted被很多人误读了的藤校 ( Ivy League )Web开发人员的10个数据库优化最佳实践《当幸福来敲门》英文片名为啥是 happyness,而不是 happiness?营销周报 | 「王老吉」发布高考定制罐;lululemon回应“以次充好被罚”;雀巢回应工厂发现可卡因;李易峰及工作室被浪莎起诉Shanghai Offers Hassle-Free ‘Hukou’ to City’s Postgraduates約旦埃及2021(9)初到開羅,開羅古城一文看透 Module Federation泪眼问花花不语,遥望对岸暗思凝,老妈加油!可口可乐官宣王心凌;上海迪士尼即将恢复运营;lululemon上新泳装系列... | 刀法品牌热讯早报 | lululemon道歉下架违规产品;李宁申请注册宁咖啡商标;Gucci门店尝试接受加密货币支付对刚adidas,lululemon一夜闯入9亿留学圈少女的心…我学语文教语文的一生(37)Wheat Destroyed Before Harvest Prompts Food Crisis Discussion【7.3今日折扣】祖马珑夏季大促!低价入手巴黎世家! Lululemon官网低至5折!Barclays已开放金融、会计、金融中后台、科技类2022 InternshipAn Online Series on Male Baldness Scrutinizes Appearance AnxietyAVOD、SVOD、TVOD、PVOD:揭秘视频点播商业模式Residents Crowd COVID Test Sites to Move Across Shanghai Freely[摄影] 便携又低成本的SD卡备份方案 - WD My Passport Wireless Pro接种疫苗后如何预防血栓New Rules for Schools in Shanghai, Added Pressure for Students市场激辩:Lululemon已经是奢侈品还是昙花一现?New Sanxingdui Relics Offer More Clues of Ancient Civilization早报 | lululemon市值赶超adidas成全球第二大运动服饰集团;安踏上半年收入实现增长;雅诗兰黛再次涨价年中大促!SSENSE现有 Fear of God Essentials 22春夏大童降价,低至5折!短T仅$35!Their Secret Sales Weapons? Language LessonsMR兑现新方法:AmEx Business Checking+AmEx Business Platinum 即可1cpp兑现从计算、建模到回测:因子挖掘的最佳实践Shenzhen Funeral Home Requires COVID Test Result for Dead
logo
联系我们隐私协议©2024 redian.news
Redian新闻
Redian.news刊载任何文章,不代表同意其说法或描述,仅为提供更多信息,也不构成任何建议。文章信息的合法性及真实性由其作者负责,与Redian.news及其运营公司无关。欢迎投稿,如发现稿件侵权,或作者不愿在本网发表文章,请版权拥有者通知本网处理。