头像加载失败,排查半天:源码里明明白白写着"域名拼接"的逻辑,编译出来的产物里------这行代码消失了。不是bug,不是网络,是编译器悄悄把我的代码"优化"掉了。
现象:头像一片空白
移动端App更新后,登录进去------右上角头像空白。
正常来说,头像应该显示用户上传的照片。查网络、查接口、查权限......都没问题:
- 接口正常返回头像路径:
/uploads/avatar/1_1785567950217.png - 图片文件存在(直接访问200)
- 但页面上就是不显示
打开浏览器控制台,看到一个404:
ruby
GET http://192.168.3.150:8080/mobile/uploads/avatar/1_1785567950217.png → 404
注意看路径------多了一个/mobile。 正确的路径应该是 http://192.168.3.150:8080/uploads/avatar/...(根路径),但它被解析成了 /mobile/uploads/...(移动端目录下)。
排查:源码里明明有拼接逻辑
页面在 /mobile/ 目录下,头像用的是相对路径 /uploads/avatar/...------相对路径在子目录页面下会被错误解析。解决办法是在路径前拼上域名(origin)。
打开源码,发现这个逻辑明明写了:
javascript
// 源码(login/index.vue)
const avatarUrl = computed(() => {
if (!userInfo.avatar) return ''
if (userInfo.avatar.startsWith('http')) return userInfo.avatar
return window.location.origin + userInfo.avatar // ← 拼接域名,明明在这!
})
window.location.origin------把域名拼到相对路径前面,得到完整的 http://192.168.3.150:8080/uploads/avatar/xxx.png。逻辑是对的,代码是有的。
那为什么页面还是404?
根因:编译器把代码"优化"没了
打开编译后的产物(Vite打包生成的JS文件),搜索这段逻辑:
javascript
// 产物(编译后的 chunk)
N = computed(() => {
return userInfo.avatar || '' // ← 拼接逻辑呢?没了!
})
window.location.origin + 这一段,在编译产物里消失了。 源码里明明有,编译完只剩 userInfo.avatar 直接返回。
这是构建工具(Vite 5.12)的产物生成缺陷------某个构建环节把这段拼接逻辑丢掉了(类似的问题还有:组件事件绑定丢失、输入框属性错乱、文字组件渲染异常------同一批构建事故)。
源码是完整的,产物是残缺的。 我们一直在查"代码有没有写错"------其实代码没错,是编译过程把对的代码弄丢了。
修复:改产物 + 修源码,双保险
第一步,直接修复当前产物(生产环境跑的是产物,等不了重新构建):
javascript
// 产物修复:把拼接逻辑补回去
N = computed(() => {
const a = userInfo.avatar || ''
return a && !/^https?:/.test(a) ? window.location.origin + a : a
})
第二步,源码也要加固 ------但更重要的是:以后每次构建完,必须验证产物和源码一致(关键逻辑在产物里 grep 验证)。
三条教训
第一,源码 ≠ 运行代码。 用户跑的是编译产物,不是你的源码。"我代码里写了"不是结论------"产物里也有"才是。 排查问题永远从产物入手,不要从源码入手------源码是愿望,产物是现实。
第二,构建工具也会出错。 我们默认"编译器是可靠的"------但遇到"源码有逻辑、产物没有"的灵异现象时,要敢于怀疑构建链本身。构建产物是"编译+优化+打包"的产物,任何一环都可能吃掉代码(尤其是tree-shaking、minify这类"优化"环节------优化器觉得"没用"的代码会被删掉,即使它有用)。
第三,修复要落在"运行的东西"上。 当时如果只改源码不修产物------构建一次又可能丢一次。先让线上恢复正常(改产物),再让源码正确(改源码),最后让流程防复发(构建后自动验证)。 三层都要做,只做一层是假修复。
结尾
头像修好后,我们给构建流程加了一步:构建完成后,自动在产物里验证关键逻辑 (比如grep location.origin,找不到就报警)。
从那以后,这种"编译吃掉代码"的事再没发生过------因为构建产物不再是黑盒,每次发布都被验证过。
源码是愿望,产物是现实。别问"我是不是写错了"------去问"产物里到底有没有"。
你的代码没丢,只是编译器替你做主了。
(完)
本文是"多Agent派发系统"系列第36篇。第35篇讲AI幻觉(把见过当做过),这一篇讲构建产物丢失(把写过的当没有)------都是"现实与期望不符"的灵异事件,一个出在AI,一个出在编译器。