样式冲突太多,记一次前端CSS升级

news2024/12/20 17:09:01

目前平台前端使用的是原生CSS+BEM命名,在多人协作的模式下,容易出现样式冲突。为了减少这一类的问题,提升研效,我调研了业界上主流的7种CSS解决方案,并将最终升级方案落地到了工程中。

样式冲突的原因

目前遇到的样式冲突的原因,其实根本原因还是css样式混乱使用导致的:

1.多人协作,样式互相污染,这是项目中的主要问题。用开发规范来限定、用CR流程来保障,并不可靠
2.引用大量第三方组件库,组件库对CSS的使用不规范。比如bee.css中使用了大量!important,破坏了项目中的样式优先级;rsuit是前端非常强大的表格组件库,他的css文件中也有直接覆盖底层样式的写法label{ marign:2px }
3.直接使用组件库引入的css文件,比如import material-icons.css,如果引用顺序靠后,这些文件可能会覆盖开发人员手写的样式。
4…

调研方案

CSS作为前端三剑客之一,几乎是所有前端同学最先学习的样式表语言。在生产环境的项目工程中,很少见到直接原生使用CSS的。但目前业界还没有通用的CSS工程化方案。这篇文章先简单介绍下7种在React/Next.js中较为流行使用CSS的方式,并说说他们的优缺点。

原生 CSS

这是一种用选择器来划分css作用域的方式。

  • 缺点:

1.作用域问题 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;
} 

2.打包体积大 使用大量冗长的原生CSS,可能会导致 打出来的包变大。包越大,项目自然跑的就越慢。

CSS MODULES

这是一种在原生CSS的基础上,通过modules(也可以理解为文件)来划分CSS的作用域。

首先先建一些以.module.css结尾的文件,这些文件里的样式可以只针对某个组件(某个module)生效。这种做法在Next.js尤为常见,因为CSS modules在Next.js是可以开箱即用的。

下面是一个例子,在Home.module.cssother.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>
	)
} 
  • 优势:

1.当需要复用样式的时候,不同的组件可以import同一份样式文件,减少很多重复样式代码,减轻打包体积~
2.说到样式复用,CSS modules还有个特殊的composes属性,能引入别的module的css样式,也能重写(override)。

.page {
	composes: className from "./shared.css"
} 
  • 缺点:

1.不够“程序化” 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;
} 
  • 优势:

1.可以用变量、继承、循环、函数、…等程序特性

  • 缺点:

1.学习成本 每种预处理器都有各自特定的语法,虽然是用一种类CSS的语言来编写,但总有有些差异。这意味着开发人员必须配合工具掌握新的语法。
2.样式和项目代码微微割裂 在解决完作用域、程序化问题后,样式在前端项目中完完全全的独立出来了,似乎少了一些联动能力。既然我们有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>
	)
} 
  • 优势:

1.轻松能实现的程序化能力 在sass里的程序化能力,CSS in JS都能做到,甚至更强,这种方式可以直接使用JS书写这种程序化语言,也不需要额外学习成本。
2.创建动态样式 在sass里,程序代码或许和样式文件是完全独立开来的。而使用CSS in JS,样式和JS强绑定,我们的样式能够跟着代码、跟着组件的state等特性实现动态样式,特别灵活!
3.不会有作用域问题 类似module,CSS in JS的样式只会绑定在样式定义的组件内,不会污染全局样式~

  • 缺点:

1.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>
	)
} 
  • 缺点

1.需要比较麻烦的额外配置
2.打包后,生成的HTML文件可读性非常非常低
3.没有任何的内置组件

  • 优势

1.打包时,能自动优化,去除没有使用的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>
	)
} 
  • 缺点:

1.在只使用bootstrap来搭建组件和修改样式的话,会不太方便 由于这类框架已经自带了许多预留组件,而bootstrap的样式又是用classname来获取的。假设我需要频繁使用<Bottom />组件,却又不想在每次使用的时候,都重复的写相同的classname,那么就会将他们封装成<CustomButtom />。这么做的话,项目代码中就可能会有大量的仅仅是为了封装classname而存在的组件。
2.打包文件过大 整个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的调研,以及对项目进行升级和改造的工作。

最后

最近还整理一份JavaScript与ES的笔记,一共25个重要的知识点,对每个知识点都进行了讲解和分析。能帮你快速掌握JavaScript与ES的相关知识,提升工作效率。



有需要的小伙伴,可以点击下方卡片领取,无偿分享

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/371458.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数组:二分查找、移除数组等经典数组题

二分查找&#xff1a;相关题目链接&#xff1a;https://leetcode.cn/problems/binary-search/题目重现&#xff1a;给定一个 n 个元素有序的&#xff08;升序&#xff09;整型数组 nums 和一个目标值 target &#xff0c;写一个函数搜索 nums 中的 target&#xff0c;如果目标值…

【c++】文件操作(文本文件、二进制文件)

文章目录文件操作文本文件写文件读文件二进制文件写文件读文件文件操作 程序运行时产生的数据都属于临时数据&#xff0c;程序一旦运行结束都会被释放&#xff1b; 通过文件可以将数据持久化&#xff1b; c中对文件操作需要包含头文件 文件类型分为两种&#xff1a; 1、文本文…

Playbook的用法

目录 Playbook Playbook 与 Ad-Hoc 对比 YAML 语言特性 YAML语法简介 支持的数据类型 写法格式 1 scalar 标量 建议缩进两个空格&#xff0c;可多 2 Dictionary 字典 3 List 列表 三种常见的数据格式 Playbook 核心组件 不要用 tab 可以#注释 hosts remote_us…

【编程入门】应用市场(Python版)

背景 前面已输出多个系列&#xff1a; 《十余种编程语言做个计算器》 《十余种编程语言写2048小游戏》 《17种编程语言10种排序算法》 《十余种编程语言写博客系统》 《十余种编程语言写云笔记》 《N种编程语言做个记事本》 目标 为编程初学者打造入门学习项目&#xff0c;使…

【Linux】进程间通信(匿名管道和命名管道通信、共享内存通信)

文章目录1、进程间通信1.1 进程的通信1.2 如何让进程间通信&#xff1f;1.3 进程间通信的本质2、管道通信2.1 匿名管道2.2 匿名管道通信2.3 命名管道2.4 命名管道的通信3、SystemV中的共享内存通信3.1 共享内存3.2 共享内存的通信3.3 共享内存的缺点以及数据保护3.4 共享内存的…

13.STM32超声波模块讲解与实战

目录 1.超声波模块讲解 2.超声波时序图 3.超声波测距步骤 4.项目实战 1.超声波模块讲解 超声波传感器模块上面通常有两个超声波元器件&#xff0c;一个用于发射&#xff0c;一个用于接收。电路板上有4个引脚&#xff1a;VCC GND Trig&#xff08;触发&#xff09;&#xff…

gcc的使用,调试工具gdb的使用

gcc编译 gcc编译可以分为四个步骤&#xff0c;预处理、编译、汇编、链接。 预处理命令&#xff1a;gcc -E hello.c -o hello.i编译命令&#xff1a;gcc -S hello.i -o hello.s汇编命令&#xff1a; gcc -c hello.s -o hello.o链接命令&#xff1a;gcc hello.o -o hello gcc…

一个.Net Core开发的,撑起月6亿PV开源监控解决方案

更多开源项目请查看&#xff1a;一个专注推荐.Net开源项目的榜单 项目发布后&#xff0c;对于我们程序员来说&#xff0c;项目还不是真正的结束&#xff0c;保证项目的稳定运行也是非常重要的&#xff0c;而对于服务器的监控&#xff0c;就是保证稳定运行的手段之一。对数据库、…

Navicat16实用小技巧

数据库管理工具是一种用于管理数据库的软件工具&#xff0c;它可以帮助用户创建、修改、维护和查询数据库。数据库管理工具可以为用户提供可视化界面&#xff0c;使得管理数据库变得更加容易。最为一款数据库管理工具&#xff0c;需要具备一下功能&#xff1a; 数据库创建和配…

OAK相机如何将yoloV6模型转换成blob格式?(2.0 及之后版本)

编辑&#xff1a;OAK中国 首发&#xff1a;oakchina.cn 喜欢的话&#xff0c;请多多&#x1f44d;⭐️✍ 内容可能会不定期更新&#xff0c;官网内容都是最新的&#xff0c;请查看首发地址链接。 ▌前言 Hello&#xff0c;大家好&#xff0c;这里是OAK中国&#xff0c;我是助手…

【对比学习论文】SimCLR 视觉表征对比学习的简单框架

文章题目&#xff1a;A Simple Framework for Contrastive Learning of Visual Representations时间&#xff1a;2020 摘要 本文提出了SimCLR:一种用于视觉表征对比学习的简单框架。我们简化了最近提出的对比自监督学习算法&#xff0c;而不需要专门的架构或内存库。为了了解…

websocket报错集锦-不断更新中

问题1&#xff1a;Failed to construct ‘WebSocket’: An insecure WebSocket connection may not be initiated from a page loaded over HTTPS. 问题描述 Mixed Content: The page at https://AAAAAA.com was loaded over HTTPS, but attempted to connect to the insecur…

Linux系统下命令行安装MySQL5.7+详细步骤

一起学编程&#xff0c;让生活更随和&#xff01; 如果你觉得是个同道中人&#xff0c;欢迎关注博主 gzh &#xff1a;【随和的皮蛋桑】。 专注于Java基础、进阶、面试以及计算机基础知识分享&#x1f433;。偶尔认知思考、日常水文&#x1f40c;。 目录1、下载安装包2、检查…

STM32 使用microros与ROS2通信

本文主要介绍如何在STM32中使用microros与ROS2进行通信&#xff0c;在ROS1中标准的库是rosserial,在ROS2中则是microros,目前网上的资料也有一部分了&#xff0c;但是都没有提供完整可验证的demo&#xff0c;本文将根据提供的demo一步步给大家进行演示。1、首先如果你用的不是S…

JUC 之 线程局部变量 ThreadLocal

—— ThreadLocal 基本概念 ThreadLocal 提供线程局部变量。这些变量与正常的变量不同&#xff0c;因为每一个线程在访问 ThreadLocal 实例的时候&#xff08;通过其get 或者 set 方法&#xff09;都有自己的、独立初始化的变副本。ThreadLocal实例通常是类中的私有静态字段&…

104-JVM优化

JVM优化为什么要学习JVM优化&#xff1a; 1&#xff1a;深入地理解 Java 这门语言 我们常用的布尔型 Boolean&#xff0c;我们都知道它有两个值&#xff0c;true 和 false&#xff0c;但你们知道其实在运行时&#xff0c;Java 虚拟机是 没有布尔型 Boolean 这种类型的&#x…

@Autowired和@Resource的区别

文章目录1. Autowired和Resource的区别2. 一个接口多个实现类的处理2.1 注入时候报错情况2.2 使用Primary注解处理2.3 使用Qualifer注解处理2.4 根据业务情况动态的决定注入哪个serviceImpl1. Autowired和Resource的区别 Aurowired是根据type来匹配&#xff1b;Resource可以根…

数据结构栈的经典OJ题【leetcode最小栈问题大剖析】【leetcode有效的括号问题大剖析】

目录 0.前言 1.最小栈 1.1 原题展示 1.2 思路分析 1.2.1 场景引入 1.2.2 思路 1.3 代码实现 1.3.1 最小栈的删除 1.3.2 最小栈的插入 1.3.3 获取栈顶元素 1.3.4 获取当前栈的最小值 2. 有效的括号 0.前言 本篇博客已经把两个关于栈的OJ题分块&#xff0c;可以根据目…

【蓝牙mesh】Upper协议层介绍

【蓝牙mesh】Upper协议层介绍 Upper层简介 Upper协议层用于处理网络层以上的功能&#xff0c;包括设备的应用层数据、安全、群组等信息&#xff0c;是实现蓝牙mesh应用功能的关键协议之一。Upper层接收来自Access层的数据或者是Upper层自己生成的Control数据&#xff0c;并且将…

typing库

typing 库 引入 在日常代码编写中&#xff0c;由于python语言特性&#xff0c;不用像go等编译性语言一样&#xff0c;在定义函数时就规范参数和放回值的类型。 def demo(a, b):return "ab" 此时 a 和 b 可以传入任意类型参数毫无疑问&#xff0c;这一特性&#…