Leaflet.js在Vue项目切换多图层时缩放功能异常问题求助
Vue项目中Leaflet切换图层后缩放异常解决方案
问题场景
在Vue项目中使用Leaflet.js渲染地图,多个页面都需要用到两种图层切换功能,但是切换图层后缩放功能出现异常,排除多类常规解决方案后问题仍然存在,一度怀疑是Leaflet 1.7.1版本存在兼容问题。
原始问题代码
<template> <div id="mapContainer" class="w-full h-full z-10"></div> </template> <script> import 'leaflet/dist/leaflet.css'; import 'leaflet-draw/dist/leaflet.draw.css'; import L from 'leaflet'; import 'leaflet-draw'; // import translate from '../translate/MapDraw'; export default { props: { useUserLocation: { type: Boolean, default: false, }, }, data() { return { map: null, coords: null, }; }, methods: { async getUserLocation() { try { const geolocation = await new Promise((resolve, reject) => { navigator.geolocation.getCurrentPosition(resolve, reject); }); this.coords = geolocation.coords; } catch (error) { this.coords = { latitude: -20.7542, longitude: -42.8819 }; } }, }, async mounted() { if (this.useUserLocation) { await this.getUserLocation(); } else { this.coords = { latitude: -20.7542, longitude: -42.8819 }; } const osm = L.tileLayer('https://{s}.tile.osm.org/{z}/{x}/{y}.png', { minZoom: 6, attribution: '© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors', }); const satellite = L.tileLayer.wms( 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', { minZoom: 6, maxZoom: 16, attribution: 'Tiles © Esri — Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community', }, ); // 定义并初始化地图 this.map = L.map('mapContainer', { center: [this.coords.latitude, this.coords.longitude], zoom: 15, layers: [osm], attributionControl: true, }); const baseMaps = { 'Open Street Map': osm, '卫星视图': satellite, }; L.control.layers(baseMaps, null, { position: 'topright', collapsed: true }).addTo(this.map); }, beforeUnmount() { if (this.map) { this.map.remove(); } }, }; </script>
报错信息

问题根因
该问题和Leaflet 1.7.1版本无关,根源是Vue data返回的对象会被Proxy代理,和Leaflet构造函数返回的地图这类复杂对象兼容性不好,Proxy的拦截机制会干扰Leaflet内部对地图实例的属性读写,导致缩放、图层切换等操作异常。
可行规避方案
核心思路是避开通过Proxy代理对象操作地图实例,初始化地图时先用局部变量存储原始实例,操作完成后再挂载到data上,销毁时用toRaw获取原始实例再执行销毁操作,修改后的代码如下:
<script> import { toRaw } from 'vue'; import 'leaflet/dist/leaflet.css'; import 'leaflet-draw/dist/leaflet.draw.css'; import L from 'leaflet'; import 'leaflet-draw'; // import translate from '../translate/MapDraw'; export default { props: { useUserLocation: { type: Boolean, default: false, }, }, data() { return { map: null, coords: null, }; }, methods: { async getUserLocation() { try { const geolocation = await new Promise((resolve, reject) => { navigator.geolocation.getCurrentPosition(resolve, reject); }); this.coords = geolocation.coords; } catch (error) { this.coords = { latitude: -20.7542, longitude: -42.8819 }; } }, }, async mounted() { if (this.useUserLocation) { await this.getUserLocation(); } else { this.coords = { latitude: -20.7542, longitude: -42.8819 }; } const osm = L.tileLayer('https://{s}.tile.osm.org/{z}/{x}/{y}.png', { minZoom: 6, attribution: '© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors', }); const satellite = L.tileLayer.wms( 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', { minZoom: 6, maxZoom: 16, attribution: 'Tiles © Esri — Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community', }, ); // 定义并初始化地图,先用局部变量存储原始实例,避免直接操作代理对象 const map = L.map('mapContainer', { center: [this.coords.latitude, this.coords.longitude], zoom: 15, layers: [osm], attributionControl: true, }); const baseMaps = { 'Open Street Map': osm, '卫星视图': satellite, }; L.control.layers(baseMaps, null, { position: 'topright', collapsed: true }).addTo(map); this.map = map; }, beforeUnmount() { if (this.map) { // 取原始实例执行销毁,避免代理对象导致的销毁异常 toRaw(this.map).remove(); } }, }; </script>
内容的提问来源于stack exchange,提问作者Erick Figueiredo
相关产品推荐
相关产品推荐

