经纬志
首页 / 实战手册 / Leaflet 容器与图层

标记图标全变破图,打包后路径丢了

先看结论

本地开发时标记是正常的定位针,打包上线后全部变成浏览器默认的破图图标。

最后验证 2026-11-03 · 多技术栈 · 更新 2026-10-29 · 引用本文

现象

每个标记位置出现一个破损图片,控制台有大量 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)。