SCSS中rem函数必须进行类型与单位校验,否则传入无单位数字或非px值将报错;基准值须为无单位数字(如$rem-base: 100),JS动态设置html font-size严格对齐该基准,且函数需全局注入。
直接写 @function rem($px) { @return $px / 100 * 1rem; } 看似简洁,但一旦传入 14(无单位)或 1.5rem,编译就报错 Incompatible units。SCSS不自动补单位,也不做隐式转换。
正确做法是加类型和单位校验:
type-of($px) == "number" 排除非数字输入unit($px) == "px" 确保只处理带 px 单位的值14)主动警告,不静默转成 14px
1rem 的合法CSS值,不能是纯数字示例修正式版:
@function rem($px) {@if type-of($px) != "number" {@warn "rem() expects a number, got #{type-of($px)}";@return $px;}@if unit($px) != "px" {@warn "rem() expects px unit, got #{unit($px)}";@return $px;}@return ($px / 100) * 1rem;}
很多人把 $rem-base: 37.5px 写进变量,结果所有 rem(750) 都算成 20rem —— 因为 750px / 37.5px = 20,但 JS 实际设置的是 html { font-size: 37.5px; },1rem 就真是 37.5px,这没问题;可一旦换设计稿宽为 640px,JS 改设 font-size: 64px,而 SCSS 还按 37.5 算,尺寸就全乱了。
真正安全的做法是:
$rem-base: 100(纯数字,无单位)document.documentElement.style.fontSize = clientWidth / 7.5 + "px" → 保证 1rem = 100px$px / $rem-base,不掺杂任何像素字面量看到 “iPhone X 屏幕窄,字号该小一点”,就想着在 @media (max-width: 375px) 里重定义 $rem-base: 90 —— 这完全无效。SCSS 变量在编译期就固化了,媒体查询块里的变量重声明,只作用于该块内新生成的 CSS,不会回溯修改之前已计算好的 rem() 值。
真正起效的路径只有一条:
resize 和 DOMContentLoaded,实时重设 html.style.fontSize
document.documentElement.clientWidth(非 window.innerWidth),排除滚动条干扰fontSize = Math.max(12, clientWidth / 7.5)
在 .vue 文件的 <style lang="scss"> 里写 font-size: rem(20); 报错 Undefined function "rem",不是函数写得不对,而是根本没被加载。
必须显式配置预处理器注入:
vue.config.js 的 css.loaderOptions.sass.prependData 中导入vite.config.ts 的 css.preprocessorOptions.scss.additionalData 中导入@import "@/styles/functions.scss";,且该文件不能含任何 html { ... } 规则@use 或 @forward —— 它们不支持跨文件全局函数调用最易忽略的一点:函数文件里不能有 @import 循环引用,否则整个 SCSS 编译链会静默失败,错误提示极不明确。