根本原因是@media未平级输出,违反CSS标准,iOS Safari等浏览器直接忽略;需用@at-root(with:media)强制提级,确保编译后@media顶格、紧跟{,且调用必须在合法选择器内并正确导入mixin。

根本原因不是 @mixin 本身有问题,而是它被调用后生成的 @media 规则仍被嵌套在选择器块内——浏览器(尤其是 iOS Safari)直接忽略这种非法结构,真机白屏或样式回退。
为什么编译后 @media 还是嵌套的?
你写了 @include respond-to(md),但 Sass 编译器会把它展开到当前选择器作用域里。如果调用位置是 .card { @include respond-to(md) { ... } },最终输出就是:
.card {
/* 基础样式 */
}
.card {
@media (min-width: 768px) {
/* 展开内容 */
}
}这违反了 CSS 标准:@media 必须是顶层规则,不能缩进在 .card { } 内部。
- DevTools 模拟时可能“宽容”解析,但真机 Safari、Android WebView 会静默丢弃整条
@media块 - 搜索编译后的 CSS 文件,确认每条
@media是否以@media开头、顶格、后面紧跟{ - VS Code 开启「显示空白字符」(
Ctrl+Shift+P→Toggle Render Whitespace),检查@media前有没有隐藏空格或制表符
@at-root (with: media) 是什么?
它是强制把 @media 提升为顶层规则的语法糖。不依赖调用位置,只认 @content 里有没有 @media。
立即学习“前端免费学习笔记(深入)”;
安全写法示例:
@mixin respond-to($breakpoint) {
@media (min-width: map-get($breakpoints, $breakpoint)) {
@at-root (with: media) {
@content;
}
}
}- 必须配合
@at-root (with: media)使用,否则@content仍会被挂载到调用点的选择器下 - 不要在
@content内再写@media,否则嵌套层级失控 - 旧版 Sass(如 Node Sass)不支持
@at-root (with: media),需升级到 Dart Sass
为什么 @include 写对了还是没输出?
@mixin 定义 ≠ 自动生效,它只是模板。未调用、调用失败、或上下文非法,都会导致最终 CSS 里压根没有那条 @media。
-
@include必须写在合法选择器内部(如.btn { @include ... }),不能放在文件顶层或@if外部 - 变量未定义就传入:
$md没声明却写@include respond-to($md)→ 编译中断,后续所有样式丢失 - 拼写错误:
@include resond-to(md)(少一个p)→ 静默失败,无报错也无输出 - 模块未导入:
@mixin在_mixins.scss里,但主文件没@use "_mixins"→ 编译器根本不知道这个@mixin存在
移动端失效最常被忽略的一环
即使 @media 编译正确、位置正确、语法正确,<meta name="viewport"> 缺失或错位,也会让整个媒体查询逻辑归零。
- 必须放在
最顶部,且在所有<style></style>和<link rel="stylesheet">之前 - 拼写零容忍:
name="viewpoint"、content="width=device"、content="width=375"全部失效 - Next.js / UniApp 等框架可能自动生成一条,你手动再加一条 → 后加载的会覆盖前一个,务必查看真机源码确认最终只有一条且内容完整
- 调试快捷方式:在真机控制台运行
document.documentElement.clientWidth,返回值不是设备逻辑宽度(如 390/414),说明viewport没起效


















