记录--记一次前端CSS升级
这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助

目前平台前端使用的是原生CSS+BEM命名,在多人协作的模式下,容易出现样式冲突。为了减少这一类的问题,提升研效,我调研了业界上主流的7种CSS解决方案,并将最终升级方案落地到了工程中。
样式冲突的原因
目前遇到的样式冲突的原因,其实根本原因还是css样式混乱使用导致的:
- 多人协作,样式互相污染,这是项目中的主要问题。用开发规范来限定、用CR流程来保障,并不可靠
- 引用大量第三方组件库,组件库对CSS的使用不规范。比如bee.css中使用了大量
!important,破坏了项目中的样式优先级;rsuit是前端非常强大的表格组件库,他的css文件中也有直接覆盖底层样式的写法label{ marign:2px } - 直接使用组件库引入的css文件,比如
import material-icons.css,如果引用顺序靠后,这些文件可能会覆盖开发人员手写的样式。 - ...
调研方案
CSS作为前端三剑客之一,几乎是所有前端同学最先学习的样式表语言。在生产环境的项目工程中,很少见到直接原生使用CSS的。但目前业界还没有通用的CSS工程化方案。这篇文章先简单介绍下7种在React/Next.js中较为流行使用CSS的方式,并说说他们的优缺点。
原生 CSS
这是一种用选择器来划分css作用域的方式。
- 缺点:
- 作用域问题 CSS样式之间会层叠覆盖,需要用大量的classname来指定选择器,来限定CSS的作用域范围。频繁的命名给开发人员增加不少心智负担,而且容易搞错搞重复。
// pure css example
.card {
/* styles */
}
.card__header {
/* styles */
}
.card--focus {
/* styles */
}
采用BEM规则来进行命名或许会简单些。 但在需要维护特别多样式的时候,BEM还是不够用。尤其是当代码中开始大量出现!important这种破坏优先级的东西的时候。
// css with !important
.card {
color: blue !important;
}
.card {
color: red;
}
- 打包体积大 使用大量冗长的原生CSS,可能会导致 打出来的包变大。包越大,项目自然跑的就越慢。
CSS MODULES
这是一种在原生CSS的基础上,通过modules(也可以理解为文件)来划分CSS的作用域。
首先先建一些以.module.css结尾的文件,这些文件里的样式可以只针对某个组件(某个module)生效。这种做法在Next.js尤为常见,因为CSS modules在Next.js是可以开箱即用的。
下面是一个例子,在Home.module.css和other.module.css对同样的类名书写样式,也不会产生冲突。
@file Home.module.css
.page {
color: bule;
}
@file other.module.css
.page {
color: yellow;
}
// 只会生效这里import的样式
import styles from '../styles/Home.module.css';
export default function Home(){
return (
// 蓝色
<div className={styles.page}>
<h1> Home Page </h1>
<div>
)
}
- 优势:
- 当需要复用样式的时候,不同的组件可以import同一份样式文件,减少很多重复样式代码,减轻打包体积~
- 说到样式复用,CSS modules还有个特殊的composes属性,能引入别的module的css样式,也能重写(override)。
.page {
composes: className from "./shared.css"
}
- 缺点:
- 不够“程序化” CSS modules在原生CSS的基础上增加了以modules(文件)划分的作用域,解决了作用域问题,但仍逃不过在单个module内以原生的方式书写CSS。原生的CSS只能纯纯的枚举出每一条样式,如果能在书写CSS的时候也支持一些程序特性岂不是更好?比如最常用的循环、遍历、函数、继承...
CSS PREPROCESSOR 预处理器
Sass、Less、Stylus... 这些预处理器就是为了解决CSS不够“程序化”而诞生的。他们允许你用一种不一样的语法来写CSS,之后再经过编译转化成原生CSS。
这里是一个例子:
// 只需一键安装sass
$ npm install sass
// 然后把原本的css后缀文件改成scss
// 就可以直接使用sass的方式来编写css啦,比如变量名、循环、...
@ file Home.module.scss $ primary-color: red;
$ font-stack:Helvetica body {
font: 100% $font-stack;
color: $primary-color;
}
- 优势:
- 可以用变量、继承、循环、函数、...等程序特性
- 缺点:
- 学习成本 每种预处理器都有各自特定的语法,虽然是用一种类CSS的语言来编写,但总有有些差异。这意味着开发人员必须配合工具掌握新的语法。
- 样式和项目代码微微割裂 在解决完作用域、程序化问题后,样式在前端项目中完完全全的独立出来了,似乎少了一些联动能力。既然我们有JSX这样整合JS和HTML的合体语言,为什么不能把CSS也合体进来呢?
CSS IN JS
这是一种把CSS写进JS的解决方案,就像把HTML写进JS后就有了JSX。这一类的库有styled components、emotion、jss、style tron、...
举个使用styled jsx的例子:
import styles from '../styles/Home.module.css';
export default function Home(){
const [color, serColor] = useState('orange');
return (
<div className={styles.page}>
<style jsx>{`
h1 {
// 取的是组件state,可以随state变化!
color: ${color};
}
`}</style>
<h1> Home Page </h1>
<div>
)
}
- 优势:
- 轻松能实现的程序化能力 在sass里的程序化能力,CSS in JS都能做到,甚至更强,这种方式可以直接使用JS书写这种程序化语言,也不需要额外学习成本。
- 创建动态样式 在sass里,程序代码或许和样式文件是完全独立开来的。而使用CSS in JS,样式和JS强绑定,我们的样式能够跟着代码、跟着组件的state等特性实现动态样式,特别灵活!
- 不会有作用域问题 类似module,CSS in JS的样式只会绑定在样式定义的组件内,不会污染全局样式~
- 缺点:
- CSS和JS混写,代码管理困难。
UTILITY CLASSES 原子类
时下最火的新概念就是tailwindcss、windi css这些原子类CSS库,能够提供大量的原子类样式,帮助我们快速构建样式。
// 配置好tailwind之后
export default function Home(){
return (
// 在这里写上tailwind的原子类classname,而不需要写样式
<div className="text-5xl font-bold">
<h1> Home Page </h1>
<div>
)
}
- 缺点
- 需要比较麻烦的额外配置
- 打包后,生成的HTML文件可读性非常非常低
- 没有任何的内置组件
- 优势
- 打包时,能自动优化,去除没有使用的css样式,减轻打包产物体积。
CSS FRAMEWORK
bootstrap、bulma、这一类库既能提供特定的样式主题,又有内置的组件,比如bottom、cards、...等等。我个人在自己倒腾东西的时候非常喜欢用这一类框架,因为实在是太方便啦!这种方式在生产上几乎很少采用,因为开发人员往往需要根据产品原型来绘制前端界面,而不是这些框架固定的样式。另外采用这种方式,也容易对线上性能造成比较大影响。
// 想使用这一类框架,只用一键安装上
$ npm install bootstrap // 引入框架的样式文件
import 'bootstrap/dist/css/bootstrap.css' export default function Home(){
return (
// 想要使用的样式都在bootstrap中用各种classname封装好啦,直接调用boostrap的预留classname,搞定
<div className="alert alert-primary">
<h1> Home Page </h1>
<div>
)
}
- 缺点:
- 在只使用bootstrap来搭建组件和修改样式的话,会不太方便 由于这类框架已经自带了许多预留组件,而bootstrap的样式又是用classname来获取的。假设我需要频繁使用
<Bottom />组件,却又不想在每次使用的时候,都重复的写相同的classname,那么就会将他们封装成<CustomButtom />。这么做的话,项目代码中就可能会有大量的仅仅是为了封装classname而存在的组件。 - 打包文件过大 整个bootstrap文件是直接import进来的。因此在打包时,会把大量没使用到的classname也打包进来,会造成打包产物较大~
组件库
这是大家最熟悉的方式啦,ant design、material design、t design、rebase、....
最终落地的升级方案
不同的CSS处理方式各有优劣,在实际开发中,可以自行选择和搭配合适的CSS处理手段。
在我目前工作中,是将项目的原生CSS,升级成css module + less 的组合,这样既能解决当前项目的核心矛盾:作用域和样式污染问题,又能让CSS的编写过程变得更“程序”,比如使用变量、继承等特性。
没有使用css in js 是因为当前项目没有主题切换和动态样式这样场景,此外css in js 会让一个组件文件变得非常冗长,尤其是目前我的工作特别多复杂图表的封装,仅jsx部分代码行数已经非常长,再引入CSS代码容易变得更混乱。我个人也更加偏向能用独立文件区分出CSS代码的方式,这样展示出更好的项目分层。
没有使用原子类的理由就更简单了,配置麻烦,可读性低,而且对团队内每个人都有较高的学习成本,不方便团队管理,直接pass了。
在前端工程开发的过程中,面对多人协作的场景,建立标准和团队内的规范是非常重要的一个环节。尤其当前业界的前端,就是没有通用标准的情况下,影响项目工程稳健性的往往是缺乏规范和标准,而不是开发人员的水平。在我工作的项目中,最初就是因为大量人员流动,大家在项目中各按各自的方式写CSS,导致在一个项目中存在3种以上CSS写法,非常难维护,也出现了样式互相污染、互相冲突的情况,所以才有了这次对CSS的调研,以及对项目进行升级和改造的工作。
本文转载于:
https://juejin.cn/post/7171413795938500639
如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。

记录--记一次前端CSS升级的更多相关文章
- 记一次前端css样式的三角形的应用
1)面试题是这样的要求用css实现 <section> <div></div> <div></div> </section> & ...
- 扯一扯前端css的整体架构设计:(2)base基础类的那些事儿
周一下午在实验室写了第一篇博文,有几个人捧场,那咱就得接着下去啊.然后我觉得现在写的内容更多的偏向于谈一下我对于前端css架构的理解和前端经验的一个小总结,所以就把标题里原来的[项目总结]给删掉了.但 ...
- {前端CSS} 语法 Css的几种引入方式 css选择器 选择器的优先级 CSS属性相关 背景属性 边框 CSS盒子模型 清除浮动 overflow溢出属性 定位(position)z-index
前端CSS CSS介绍 CSS(Cascading Style Sheet,层叠样式表)定义如何显示HTML元素,给HTML设置样式,让它更加美观. 当浏览器读到一个样式表,它就会按照这个样式表来对文 ...
- 前端CSS - 相对定位,绝对定位,固定定位
前端CSS - 相对定位,绝对定位,固定定位 1.1 相对定位 position:relative 相对定位,就是微调元素位置的.让元素相对自己原来的位置,进行位置的微调. 也就是说,如果一个盒子想进 ...
- 记一起和前端没什么卵关系的OPTION 405问题
记一起和前端没什么卵关系的后端405问题 问题的关键点在于本来是POST请求,会变成OPTION请求,并且提示405报错,会类似跨域.并且只有某些手机机型才会(如Oppo系列). 其实跨域的问题,如果 ...
- day 45 前端CSS
前端CSS CSS介绍 CSS(Cascading Style Sheet,层叠样式表)定义如何显示HTML元素,给HTML设置样式,让它更加美观. 当浏览器读到一个样式表,它就会按照这个样式 ...
- 前端CSS编程之道-LESS
由于前端css编写繁琐,最近开始学习LESS,用LESS编写文件.less文件可以直接编译成我们要的.css文件 学习Less 我下面是我练习时的截图,希望小伙伴也能动手自己写一下,而不是复制粘贴模式 ...
- Web前端-CSS必备知识点
Web前端-CSS必备知识点 css基本内容,类选择符,id选择符,伪类,伪元素,结构,继承,特殊性,层叠,元素分类,颜色,长度,url,文本,字体,边框,块级元素,浮动元素,内联元素,定位. 链接: ...
- WEB前端 CSS(非布局)
目录 WEB前端 CSS CSS引入方式 CSS结构 CSS选择器 直接选择器 组合选择器 分组选择器 也叫并集选择器 属性选择器 伪类选择器 伪元素选择器 CSS选择器是一个查找的过程,高效的查找影 ...
- 前端 CSS 目录
前端 CSS 介绍 前端 CSS语法 前端 CSS 注释
随机推荐
- NC16417 [NOIP2017]奶酪
题目链接 题目 题目描述 现有一块大奶酪,它的高度为 h,它的长度和宽度我们可以认为是无限大的,奶酪中间有许多半径相同的球形空洞.我们可以在这块奶酪中建立空间坐标系, 在坐标系中,奶酪的下表面为 z ...
- 吴X凡绯闻女友小怡同学被骂到清空社交平台?各大平台连敏感词库都没有的吗?
敏感词都没有的平台 最近某加拿大籍贯的 rapper 被曝私生活不检点,且极有可能涉及诱X未成年少女,成为一个 raper. 当然至于是否属实,其实一个人是否是海王,微信.QQ 聊天记录里面记得清清楚 ...
- Lora 模块E22-400T22D(SX1268, 22dBm)
无线串口(UART)模块, 基于SEMTECH SX1268射频芯片, 具有多种传输方式, 工作在410.125至493.125MHz频段(默认 433.125MHz). LoRa扩频技术, TTL电 ...
- Table布局
Table布局 <table>最常用的也是最正确的使用方法是制作表格,由于其对占据的空间有着划分的作用,便可以使用<table>来布局. 实例 实现一个简单的布局,将表格的bo ...
- ~Keven_He的黑历史~
"先生,我认为文言文比白话文更加简洁" "请举例" "就好像沉鱼落雁这句成语不是比白话文更加简洁吗" "沉鱼落雁是四个字,该用白话 ...
- 我的小程序之旅三:微信小程序登录流程设计
登录时序图 获取小程序的AppID和AppSecret 一.微信获取登录用户的openId 1.wx.login() { "code": "192038921jkjKHW ...
- golang生成一个dll供rundll32.exe调用
背景: 项目用例需要覆盖场景:window rundll32.exe 调用dll并写一段内容到磁盘, 开始使用msf生成,会被安全软件拦截,索性用go 写一个 代码如下: package main i ...
- 如何设置 vcpkg 依赖特定编译器
最近项目要部署到 gitlab-ci 上,所以远程机器上也要安装好编译环境 在相关的环境安装完后,发现编译项目时提示找不到三方库的符号文件 看到这个错误的第一反应就是依赖库的版本不对,因为远程机器上不 ...
- 使用 MSYS2 编译 exe 可执行程序
MSYS2 是一个在 Windows上 运行的软件环境,它提供了一种在 Windows 上使用 GNU 工具链的方式,包括 GCC 编译器和 GNU Make 构建系统. 在 MSYS2 中,你可以使 ...
- RAID 10磁盘阵列实践
RAID概述 RAID技术通过把多个硬盘设备组合成一个容量更大.安全性更好的磁盘阵列,利用分散读写技术来提升磁盘阵列整体的性能,同时把多个重要数据的副本同步到不同的物理硬盘设备上,从而起到了非常好的数 ...