
基础复用:从选择器到CSS变量,3个方法让重复代码“消失”
其实很多时候,我们写重复CSS不是因为不知道复用,而是没意识到“这些代码其实可以合并”。我带过几个刚入行的实习生,看他们写CSS时,明明多个元素用了相同的样式,却非要分开写,问他们为什么,都说“感觉这样看得清楚”。后来我让他们试着把重复样式合并,代码量直接少了三分之一,改样式时也不用来回切换文件了。下面这3个基础方法,不用学复杂工具,现在打开你的项目就能用起来。
选择器合并:一句话搞定多个元素的相同样式
最直接也最容易被忽略的复用方法,就是选择器合并。简单说,就是把需要用相同样式的多个选择器,用逗号隔开写在一起,这样一套样式就能作用于多个元素。比如你有按钮、链接、导航项,它们的 hover 效果都是“颜色变深、加阴影”,完全不用写三套样式,像这样合并起来:
.btn, .nav-link, .card-title {
transition: all 0.3s ease;
}
.btn:hover, .nav-link:hover, .card-title:hover {
color: #2c3e50;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
我之前做一个电商网站时,商品卡片、用户头像、分类标签都用了相同的圆角和边框,一开始实习生每个类都写了border-radius: 8px; border: 1px solid #eee;
,光这两行就重复了十几遍。后来用选择器合并改成:
.goods-card, .user-avatar, .category-tag {
border-radius: 8px;
border: 1px solid #eee;
}
代码瞬间清爽多了。不过要注意,选择器合并适合“完全相同”的样式,如果只是部分相同,比如有的元素需要加 padding,有的不需要,就别硬合并,反而会让代码更乱。你可以现在就打开自己的CSS文件,搜索一下有没有重复的样式块,把选择器合并试试,绝对能立刻看到效果。
CSS变量:一次定义,全局使用,改样式只改一行代码
如果说选择器合并是“横向复用”(多个元素用同一套样式),那CSS变量(Custom Properties) 就是“纵向复用”(多个地方用同一个值)。你有没有遇到过“主题色改了,所有用到这个颜色的地方都要改”的情况?我之前帮一个博客网站做改版,他们要把主色调从红色改成蓝色,结果发现按钮、标题、边框、链接都用了红色,我改了27处才改完,改到最后都怕漏了哪个角落。要是早用CSS变量,根本不用这么麻烦。
CSS变量的用法很简单,先在:root
里定义变量(变量名要以开头),比如:
:root {
primary-color: #3498db; / 主色调 /
border-radius: 8px; / 统一圆角 /
spacing-sm: 8px; / 小间距 /
spacing-md: 16px; / 中间距 /
}
然后在需要的地方用var()
调用,比如按钮样式:
.btn {
background-color: var(primary-color);
border-radius: var(border-radius);
padding: var(spacing-sm) var(spacing-md);
}
这样一来,不管有多少个元素用了primary-color
,只要在:root
里改一次,所有地方都会同步更新。我去年用这个方法帮朋友的电商网站做“夜间模式”,白天模式用白色背景、黑色文字,夜间模式用深灰背景、浅色文字,只需要用JS动态修改CSS变量的值,整个网站的主题就能切换,朋友当时都惊呆了:“原来不用写两套CSS啊!”
不过要注意,CSS变量是有作用域的,在:root
里定义是全局变量,在某个类里定义就是局部变量,只在该类内部生效。比如你想给某个模块单独设置不同的间距,可以这样:
.card-special {
spacing-md: 24px; / 局部覆盖全局变量 /
padding: var(spacing-md);
}
这种灵活性让CSS变量既能统一全局样式,又能满足局部特殊需求,强烈 你现在就把项目里的颜色、间距、圆角这些“重复出现的值”都换成CSS变量,以后改样式绝对会感谢自己。
样式继承:让子元素“自动”拥有父元素的样式
除了主动复用,CSS本身就有“被动复用”的机制——样式继承。简单说,父元素的某些样式会自动“传给”子元素,不用我们手动给每个子元素写样式。最常见的就是文本相关样式,比如font-family
(字体)、color
(文字颜色)、line-height
(行高),这些属性在父元素设置后,子元素会默认继承。
比如你在body
里设置:
body {
font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif;
color: #333;
line-height: 1.5;
}
那么页面里的p
、span
、h1-h6
等文本元素,就都会自动用这些样式,不用一个个设置。我刚工作时不知道这个,傻乎乎地给每个p
标签都写font-family
,后来被带我的前辈看到,他笑着说:“你这是在给CSS‘打工’啊,让它自己‘继承’不就行了?”
不过不是所有CSS属性都能继承,比如border
、padding
、margin
这些盒模型相关的属性就不会继承。如果想让某个不继承的属性强制继承,可以用inherit
关键字,比如让子元素继承父元素的边框颜色:
.child {
border-color: inherit; / 强制继承父元素的border-color /
}
合理利用继承,能减少很多重复的文本样式代码。你可以现在打开浏览器的开发者工具,看看自己项目里的文本元素,是不是有很多重复设置的font-family
或color
,试着删掉这些重复样式,让它们通过继承获取,代码会干净很多。
进阶复用:预处理器与模块化,解决复杂项目的“样式混乱”
基础方法能解决大部分简单场景,但如果项目比较复杂,比如有几十个页面、上百个组件,光是选择器合并和CSS变量可能还不够。你有没有遇到过“写了一个样式,结果影响到其他页面”的情况?或者“想复用某个组件的样式,又怕改了之后其他地方跟着变”?我之前参与一个大型后台管理系统开发时,团队里10多个前端,每个人写的CSS都有自己的“风格”,有的用BEM命名,有的直接写标签选择器,后来页面越来越多,样式冲突成了家常便饭,改一个按钮样式,可能会导致某个表格的边框消失,简直崩溃。后来我们引入了预处理器和模块化方案,才彻底解决了这些问题。
预处理器:用@mixin和@extend,让复用代码“带参数”
CSS预处理器(比如Sass、Less)是对原生CSS的扩展,增加了变量、嵌套、混合(mixin)等功能,其中混合(mixin) 是复用代码的“神器”。和CSS变量不同,mixin可以复用一整块样式,还能传参数,实现“带变化的复用”。比如你有多个按钮,大小不同(小按钮、中按钮、大按钮),但样式逻辑相同(背景色、圆角、hover效果),用原生CSS可能要写三套几乎一样的代码,而用Sass的@mixin,只需要定义一个“模板”:
@mixin btn-style($size) {
background-color: #3498db;
border-radius: 4px;
color: white;
padding: if($size == 'small', '8px 12px', if($size == 'medium', '12px 16px', '16px 20px'));
font-size: if($size == 'small', 14px, if($size == 'medium', 16px, 18px));
cursor: pointer;
&:hover {
opacity: 0.9;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
}
然后用@include
调用,传不同的参数生成不同大小的按钮:
.btn-small { @include btn-style('small'); }
.btn-medium { @include btn-style('medium'); }
.btn-large { @include btn-style('large'); }
这样一来,不管有多少种按钮尺寸,都能通过一个mixin生成,而且修改样式逻辑时,只需要改mixin里的代码。我之前用这个方法做过一个表单页面,里面有10多种输入框(文本框、下拉框、日期选择器),它们的聚焦样式(边框变色、阴影)都一样,只是高度不同,用mixin传高度参数,瞬间就搞定了,比写10套重复样式效率高多了。
除了@mixin,Sass的@extend也能复用样式,它的作用是“继承”另一个选择器的样式,比如:
.base-btn {
border: none;
border-radius: 4px;
cursor: pointer;
}
.btn-primary {
@extend .base-btn;
background-color: #3498db;
color: white;
}
.btn-secondary {
@extend .base-btn;
background-color: #95a5a6;
color: black;
}
编译后,.btn-primary
和.btn-secondary
会继承.base-btn
的所有样式,相当于:
.base-btn, .btn-primary, .btn-secondary {
border: none;
border-radius: 4px;
cursor: pointer;
}
不过要注意,@extend更适合“完全复用不变的样式”,而@mixin适合“复用带参数变化的样式”。如果你不确定用哪个,可以记住一个简单原则:需要传参数就用@mixin,纯样式复用就用@extend。
模块化方案:CSS Modules与CSS-in-JS,让样式“各管各的”
不管复用得多好,如果样式作用域是全局的,就难免会出现“样式冲突”——你写了一个.card
类,结果其他页面的.card
样式和你的冲突了,导致布局错乱。我之前就遇到过,自己写的.header
样式,被同事写的全局.header
覆盖,页面顶部直接“飞”了,查了半天才发现是类名重复。后来学了CSS模块化,才知道原来样式也能“各管各的”,互不干扰。
CSS Modules的原理很简单:把CSS文件当成一个模块,每个类名会被编译成唯一的哈希值,比如你写的.card
,编译后可能变成._card_12345
,这样就算其他文件也有.card
,哈希值不同,就不会冲突。使用时,在CSS文件里写普通样式:
/ Card.module.css /
.card {
border: 1px solid #eee;
border-radius: 8px;
padding: 16px;
}
.title {
font-size: 18px;
color: #333;
}
然后在JS/TS文件里导入,通过对象的方式使用类名:
import styles from './Card.module.css';
function Card() {
return (
卡片标题
);
}
这样编译后,HTML里的类名会变成类似Card_module__card__1a2b3
的唯一值,彻底避免冲突。我现在做React项目必用CSS Modules,再也不用担心“自己的样式被别人改了”或者“不小心改了别人的样式”。
如果你用的是Vue,也有类似的方案,在标签里加
scoped
属性,就能让样式只在当前组件生效:
.card {
/ 只作用于当前组件的.card元素 /
border: 1px solid #eee;
}
除了CSS Modules,还有CSS-in-JS方案(比如styled-components、Emotion),直接在JS里写CSS,样式和组件完全绑定。比如用styled-components写一个按钮组件:
import styled from 'styled-components';
;const StyledButton = styled.button
background-color: #3498db;
border-radius: 4px;
padding: 8px 16px;
color: white;
&:hover {
opacity: 0.9;
}
function Button() {
return 点击我;
}
这种方式的好处是样式和组件“同生共死”,组件被删除,样式也一起删除,不会有冗余代码。不过学习成本比CSS Modules高一点,如果你刚接触模块化, 先从CSS Modules入手,上手更简单。
不同复用方法怎么选?一张表帮你理清
说了这么多方法,你可能会问:“这么多复用技巧,什么时候用哪个啊?”别着急,我整理了一张对比表,你可以根据自己的项目情况参考选择:
复用方法 | 适用场景 | 优势 | 局限性 | 学习难度 |
---|---|---|---|---|
选择器合并 | 多个元素用完全相同的样式 | 原生CSS支持,无需工具 | 不适合有差异的样式 | ★☆☆☆☆ |
CSS变量 | 复用颜色、间距等单值 | 动态修改方便,原生支持 | 不能复用整块样式 | ★★☆☆☆ |
@mixin(预处理器) | 复用带参数的样式块 | 支持动态变化,复用灵活 | 需要学习预处理器语法 | ★★★☆☆ |