SCSS中用@each循环生成多字重@font-face规则最简洁,但需确保每个字重对应独立字体文件、正确命名、配font-display: swap,并通过开发者工具验证实际加载字重。

SCSS中用@each循环生成多字重@font-face规则
直接写死多个@font-face太重复,用@each遍历权重数组是最干净的解法。关键不是“能不能”,而是“怎么保证浏览器真加载对应字重”。
- 必须确认每个字重都有独立的字体文件(比如
Oswald-300.woff、Oswald-400.woff、Oswald-700.woff),不能只靠一个文件硬设font-weight -
font-display: swap建议始终加上,避免FOIT(字体阻塞渲染) - 路径拼接要用
asset_url()(Rails)或url()(纯CSS/webpack),别手写相对路径,否则编译后404
示例:
$weights: (300, 400, 600, 700);
@each $w in $weights {
@font-face {
font-family: 'Oswald';
src: asset_url('Oswald-#{$w}.woff2') format('woff2'),
asset_url('Oswald-#{$w}.woff') format('woff');
font-weight: $w;
font-style: normal;
font-display: swap;
}
}
为什么font-weight: 600写了却没变粗?
不是SCSS写错了,是字体文件本身没提供600这一档——浏览器只能 fallback 到最接近的可用字重(比如400或700),视觉上毫无变化。
- 打开开发者工具 → Elements → 选中文字 → 右侧“Fonts”面板,看“Actual font”后面标注的 weight 是多少
- Google Fonts链接里漏了
wght@600参数,就别指望font-weight: 600生效 - 本地字体用
font-squirrel生成kit时,勾选“Select weights”要明确包含600,否则生成的包里根本没有
用@mixin封装多字重+多样式(斜体/可变字体)
如果还要支持italic或可变字体轴(如wdth、opsz),硬套@each会嵌套爆炸。用@mixin收口更可控。
立即学习“前端免费学习笔记(深入)”;
- 传入
$family、$weights、$styles: (normal, italic)三个参数,内部双层@each - 对
italic变体,文件名要带-Italic后缀,且font-style: italic必须显式声明 - 可变字体不用列一堆
@font-face,一个就够了:font-weight: 100 900;+font-stretch: 75% 125%;
片段示意:
@mixin font-faces($family, $weights, $styles: (normal)) {
@each $w in $weights {
@each $s in $styles {
$suffix: if($s == normal, '', '-Italic');
@font-face {
font-family: $family;
src: asset_url('#{$family}-#{$w}#{$suffix}.woff2') format('woff2');
font-weight: $w;
font-style: $s;
font-display: swap;
}
}
}
}
@include font-faces('Oswald', (300, 400, 700), (normal, italic));
生成的CSS体积和加载时机问题
每个@font-face都会触发一次字体文件HTTP请求,但浏览器实际下载是懒加载的——只在元素真正用到该font-weight时才拉取。不过CSS文件本身会变大。
- 别为“可能用到”的字重提前声明,比如只用400和700,就别把100–900全循环进去
- Rails里
asset_url()在编译期解析,若文件不存在,SASS编译直接报错,不是运行时报404 - 生产环境开启Brotli压缩,
.woff2本身已高压缩,多几条@font-face对最终体积影响极小
最易被忽略的是:字体文件命名必须和@font-face里写的完全一致,大小写、连字符、数字位数(600 vs 600pt)差一点,整个字重就失效。


















