标记图标全变破图,打包后路径丢了
本地开发时标记是正常的定位针,打包上线后全部变成浏览器默认的破图图标。
现象
每个标记位置出现一个破损图片,控制台有大量 404:
GET https://example.cn/marker-icon.png 404 (Not Found)
GET https://example.cn/marker-icon-2x.png 404 (Not Found)
GET https://example.cn/marker-shadow.png 404 (Not Found)
图片路径是站点根目录,而实际文件被构建工具放到了
/assets/marker-icon-abc123.png。
原因
Leaflet 的默认图标通过 CSS 背景图与运行时探测来确定路径,
L.Icon.Default 的 imagePath 会在页面加载时自动推断。
使用 webpack、Vite 等工具打包后,图片被重命名并移动到资源目录,
CSS 里的原始相对路径与运行时推断出的路径都失效。
另一种情况是 CDN 与被代理的部署结构:页面从 /app/ 访问,
但图标按根路径请求,需要显式指定前缀。
排查过程
打印默认图标的最终 URL,与网络面板中的请求对照:
console.log(L.Icon.Default.prototype.options);
console.log(L.Icon.Default.prototype._getIconUrl('icon'));
// https://example.cn/marker-icon.png
解决方案
方案一:显式设置图标路径前缀。路径必须是绝对路径,或能被服务器解析的完整地址:
delete L.Icon.Default.prototype._getIconUrl;
L.Icon.Default.mergeOptions({
iconRetinaUrl: '/assets/leaflet/marker-icon-2x.png',
iconUrl: '/assets/leaflet/marker-icon.png',
shadowUrl: '/assets/leaflet/marker-shadow.png',
iconSize: [25, 41],
iconAnchor: [12, 41],
popupAnchor: [1, -34],
shadowSize: [41, 41]
});
方案二:不用默认图标,直接创建一个自定义 Icon,连图片都不用:
var dotIcon = L.divIcon({
className: 'station-dot',
html: '<span></span>',
iconSize: [14, 14],
iconAnchor: [7, 7]
});
L.marker([39.907, 116.391], { icon: dotIcon }).addTo(map);
方案三:用构建工具的资源导入方式,让打包器处理图片地址:
import iconUrl from 'leaflet/dist/images/marker-icon.png';
import iconRetinaUrl from 'leaflet/dist/images/marker-icon-2x.png';
import shadowUrl from 'leaflet/dist/images/marker-shadow.png';
L.Icon.Default.mergeOptions({ iconUrl, iconRetinaUrl, shadowUrl });
验证
网络面板中三张图标全部 200,响应类型为 image/png。
标记显示为正常的定位针,阴影位置正确。
连续添加 20 个标记,图标只请求一次,说明有缓存。
别用相对路径:./marker-icon.png 在子路由下会解析到
/project/marker-icon.png。图标路径统一走绝对路径或构建变量,
是最省心的做法。
经纬志(2026)。《标记图标全变破图,打包后路径丢了》。https://www.jingweizhi.com/cookbook/leaflet-marker-icon-404.html(访问日期 2026-11-03)。