根本原因是浏览器解析url()时以最终CSS文件物理位置为基准,而非SCSS源码路径;构建后CSS与图片的相对位置变化导致路径失效,需对照dist目录真实结构反推正确层级。

SCSS里写url("../img/x.png"),为什么浏览器请求的是错路径?
根本不是SCSS编译错了,而是浏览器加载最终CSS时,url()的解析基准是那个物理存在的CSS文件位置,不是你源码里的.scss路径。比如你在src/components/Button.scss里写url("../img/icon.png"),开发时Webpack可能把它内联进JS或输出到dist/css/app.css,但构建后CSS实际在/css/app.css,而图片在/assets/img/icon.png——这时../img/icon.png就变成了/img/icon.png,404。
- 打开
dist目录,肉眼确认CSS文件和图片的真实相对位置,再反推url()该写几级.. - 别信“开发能跑=路径对”,dev server会自动代理、重写路径,掩盖问题
- Vite/Webpack默认不处理
url()里的相对路径,除非你配了css.preprocessorOptions.sass.additionalData或MiniCssExtractPlugin.publicPath
为什么用了@/assets或~@/assets还是404?
@/和~前缀只在Webpack的file-loader/url-loader或Vite的resolve.alias配置生效的前提下才被识别;如果没配,它们就是纯字符串,浏览器原样拼进URL,比如url('@/assets/bg.png')会发出请求https://example.com/@/assets/bg.png。
- Vite中必须在
vite.config.ts显式声明:resolve: { alias: { '@': path.resolve(__dirname, 'src') } } - Webpack中要配
resolve.alias,且url-loader的options.publicPath需匹配部署路径 -
~@/assets这种双前缀是无效的,~是Webpack旧约定,Vite不认;@是别名,不是语法糖
用SCSS变量统一路径真的能绕过所有坑?
能。因为变量插值(url(#{$img-path} + "logo.png"))让路径变成纯字符串拼接,跳过了构建工具对url()的路径解析逻辑,也不依赖@import或loader行为。
- 定义变量:
$img-path: "/assets/";(注意开头必须有/) - 使用:
background-image: url(#{$img-path} + "logo.png"); - 变量值必须是绝对路径(以
/开头),否则仍受CSS文件位置影响 - 别把变量放在
_variables.scss里然后@use——@use本身也有路径查找逻辑,容易二次出错;直接在根index.scss里定义最稳
public目录下放图,为什么还要加/?
public目录的资源是直接映射到域名根路径的,比如public/assets/logo.png → 可通过/assets/logo.png访问。但如果你写url("assets/logo.png")(没/),浏览器就按CSS文件位置算相对路径,大概率错。
立即学习“前端免费学习笔记(深入)”;
- 必须写
url("/assets/logo.png"),开头/表示从域名根开始 - 不要用
./或../,public的意义就是路径绝对可控,没必要退级 - 这类资源不会被哈希,适合favicon、banner等长期不变的图;频繁更新的图别放这里
url()解析,是两套完全独立的机制,中间隔着构建工具的输出结构。盯着源码改../次数再多,只要没看一眼dist里CSS和图片的物理关系,就还在盲调。


















