经纬志
首页 / 实战手册 / 工程化

主站和子系统各引一份 Leaflet,地图对象认不出来

先看结论

子系统单独跑正常,嵌进主站后地图能显示,调用 map.remove() 却报“不是函数”。

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

现象

主站用 CDN 引入了 Leaflet 1.7.1,子系统打包时又带了一份 1.9.4。 两者都挂到 window.L,后加载的覆盖先加载的。运行时报:

Uncaught TypeError: map.remove is not a function
    at MapPanel.destroy (MapPanel.vue:88)

Uncaught TypeError: layer.addTo is not a function

地图本身能渲染,说明 L.map 可用;但由另一份库创建的图层, 与当前 window.L 的类不匹配,方法自然找不到。

原因

Leaflet 是 UMD 包,通过 window.L 暴露。两份副本各自定义一套类, instanceof L.Layer 在不同副本之间为 false。 map.remove() 内部会检查图层实例的类,跨副本时判断失败。

同理,L.Icon.Default 的图标路径也会被后加载的副本覆盖, 表现为主站标记图标突然变化。

排查过程

  1. 在控制台比较两份库的版本:L.version 与打包进来的版本。
  2. 搜索 HTML 中的 leaflet.js 引用与 package.json 中的依赖。
  3. 用 L.Layer === otherL.Layer 验证是否为同一份实现。
console.log('global L', window.L && window.L.version);
console.log('bundle L', importedL.version);
console.log('同一个类?', window.L.Map === importedL.Map);   // false

解决方案

只保留一份。主站已有 Leaflet 时,子系统通过 externals 把它指向全局变量, 不再打包副本:

// vite.config.js
export default {
  build: {
    rollupOptions: {
      external: ['leaflet'],
      output: {
        globals: { leaflet: 'L' }
      }
    }
  }
};

用 webpack 时配置 externals,效果相同:

module.exports = {
  externals: {
    leaflet: 'L'
  }
};

反过来,如果子系统是独立打包、不依赖主站,则把主站的全局引用去掉, 统一用 npm 依赖,并在入口显式挂载一次:

import * as L from 'leaflet';

window.L = L;      // 只在确认没有其他副本时挂载

var map = L.map('map').setView([39.907, 116.391], 11);

多个系统必须共存时,不要共用 window,把地图模块放进 iframe 或独立的 Web Worker 之外的沙箱容器,各自持有自己的 L:

var scope = { L: myLeaflet };
scope.L.map('sub-map').setView([39.907, 116.391], 11);

验证

window.L.Map === importedL.Map 为 true。 map.remove() 正常执行,页面卸载时地图实例被销毁。 主站与子系统的标记图标样式一致,不再被覆盖。

先数副本:在控制台执行 L.version 与打包版本对比。 两份库共存时,报错通常不是“方法不存在”,而是“类型判断失败”, 容易误判成业务代码问题。

引用本文

经纬志(2026)。《主站和子系统各引一份 Leaflet,地图对象认不出来》。https://www.jingweizhi.com/cookbook/global-variable-conflict.html(访问日期 2026-11-03)。