【问题标题】:Can't load Leaflet inside Vue component无法在 Vue 组件中加载 Leaflet
【发布时间】:2022-04-24 16:06:37
【问题描述】:

我正在尝试将 Leaflet 地图创建为 Vue 组件,但我开始遇到一些困难。我通过 npm 安装了 Leaflet

我哪里出错了? console.log(Leaflet) 正在返回一个 Leaflet 对象,但我无法让地图展开和呈现。

一些方向将不胜感激

<template>
    <div id="map"></div>
</template>

<script>
    // import leaflet here?
    import Leaflet from 'leaflet';

    export default {
        components: {
            Leaflet
        },

        created() {
            console.log(this);
            console.log(Leaflet);
        },

        ready() {
            this.map = L.map('map').setView([51.959, -8.623], 14);
            L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
                attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
            }).addTo(this.map);
        }
    }
</script>

<style>
    #map {
        height: 100%;
        width: 100%;
        margin-top: -24px;
    }

    /* default legend, layer styling from leaflet template */
    .info {
        padding: 6px 8px;
        font: 14px/16px Arial, Helvetica, sans-serif;
        background: white;
        background: rgba(255,255,255,0.8);
        box-shadow: 0 0 15px rgba(0,0,0,0.2);
        border-radius: 5px;
    }
    .info h4 {
        margin: 0 0 5px;
        color: #777;
    }
    .legend {
        text-align: left;
        line-height: 18px;
        color: #555;
    }
    .legend i {
        width: 18px;
        height: 18px;
        float: left;
        margin-right: 8px;
        opacity: 0.7;
    }
</style>

【问题讨论】:

    标签: leaflet vue.js vuejs2


    【解决方案1】:

    您的代码中有几个问题:

    1. Vue 中没有ready 生命周期挂钩。使用mounted()
    2. 您正在尝试将 Leaflet 库作为组件传递给 Vue,但由于 Leaflet 不是 Vue 组件并且不需要任何形式的向 Vue 注册,因此它什么也不做。
    3. 未在 Vue 组件上声明 map 数据属性。
    4. 将库导入为Leaflet from 'leaflet' 没有问题,但您或其他人将Leaflet 对象声明为L from 'leaflet' 可能更一致。您也可以使用:import { Map } from 'leaflet',但必须适当地初始化您的地图:this.map = new Map("mapContainer")
    5. 通过在 Leaflet Map Class 上使用 remove() 方法适当地防止潜在的内存泄漏和/或清理。 Vue beforeDestroy 生命周期钩子是一个好地方。

    另外,不要忘记导入 Leaflet CSS,例如: import "leaflet/dist/leaflet.css";

    <template>
      <div id="mapContainer"></div>
    </template>
    
    <script>
    import "leaflet/dist/leaflet.css";
    import L from "leaflet";
    
    export default {
      name: "LeafletMap",
      data() {
        return {
          map: null
        };
      },
      mounted() {
        this.map = L.map("mapContainer").setView([51.959, -8.623], 12);
        L.tileLayer("http://{s}.tile.osm.org/{z}/{x}/{y}.png", {
          attribution:
            '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
        }).addTo(this.map);
      },
      beforeDestroy() {
        if (this.map) {
          this.map.remove();
        }
      }
    };
    </script>
    
    <style scoped>
    #mapContainer {
      width: 100vw;
      height: 100vh;
    }
    </style>
    

    【讨论】:

    • 当我发布这个问题时,我是 JS 新手。 2 年后,我可以确认这是正确的答案并且很有意义!
    【解决方案2】:

    jhickok 已经提供了正确答案,但对 vue3 组合 API 也很有洞察力。由于解释的相同原因,无法在 setup() 中实例化传单地图。

    <template>
      <div id="mapContainer"></div>
    </template>
    
    <script>
    import { map, tileLayer } from "leaflet";
    import { onMounted, onBeforeUnmount } from "vue";
    
    export default {
      name: "Map",
      setup() {
        let container = null;
    
        onMounted(() => {
          container = map("mapContainer").setView([51.959, -8.623], 12);
    
          tileLayer("http://{s}.tile.osm.org/{z}/{x}/{y}.png", {
            attribution:
              '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
          }).addTo(container);
        });
    
        onBeforeUnmount(() => {
          container.remove();
        });
      }
    };
    </script>
    
    <style scoped>
    #mapContainer {
      width: 40vw;
      height: 40vh;
    }
    </style>
    

    【讨论】:

      【解决方案3】:

      先用npm安装leafletjs和vue2-leaflet

      第二次复制代码到你的组件

      <template>
            <div id="app" v-if="isLoad">
              <l-map style="height: 350px" :zoom="zoom"   :center="center" @click="addMarker"   :max-zoom="maxZoom" :min-zoom="minZoom">
                <l-tile-layer :url="url" :attribution="attribution"></l-tilelayer>
                <l-marker  :lat-lng="markerLatLng" ></l-marker>
              </l-map>
            </div>
      </template>
      
      <script>
          import {LMap, LTileLayer, LMarker} from "vue2-leaflet";
          import "leaflet/dist/leaflet.css";
          import {Icon} from "leaflet";
          
          delete Icon.Default.prototype._getIconUrl;
          
          Icon.Default.mergeOptions({
            iconRetinaUrl: require("leaflet/dist/images/marker-icon-2x.png"),
            iconUrl: require("leaflet/dist/images/marker-icon.png"),
            shadowUrl: require("leaflet/dist/images/marker-shadow.png")
          });
          export default {
            components: {
              LMap,
              LTileLayer,
              LMarker,
            },
          
            mounted() {
              this.mapIsReady()
            },
            data() {
              return {
                isLoad: false,
                url: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
                attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors',
                 zoom: 15,
                maxZoom:18,
                minZoom:5,
                center: [35.6892, 51.3890],
                markerLatLng: [35.6892, 51.3890],
              };
            },
            methods: {
              mapIsReady() {
                let self = this
                setTimeout(() => self.isLoad = true, 1500)
              },
              addMarker(event) {
                console.log(event)
                this.markerLatLng = [event.latlng.lat,event.latlng.lng];
              },
          
            },
          };
      </script>
      

      此代码为您的地图添加了标记、最大缩放和最小缩放

      此代码使用开放街道地图,因为它是免费的。如果需要,您可以将其更改为 api.mapbox.com,但您需要 api 密钥或 accessToken

      【讨论】:

        【解决方案4】:

        我刚做了这个,但我也需要底图 (bing)。

        leafletWrapper.js

        const L = require('leaflet')
        require('leaflet-bing-layer')
        
        export default L
        

        LeafletMap.vue

        <template>
          <div id="map">
          </div>
        </template>
        
        <script>
        import L from 'leafletWrapper'
        
        export default {
            name: 'leaflet-map',
            props: ['center', 'zoom', 'minZoom', 'maxZoom', 'markers'],
            data () {
              return {
                mapObject: {},
                markerLayer: {}
              }
            },
         mounted () {
              const bingKey = 'foo'
        
              this.mapObject = L.map(
                "map", {
                  center: this.center,
                  zoom: this.zoom,
                  minZoom: this.minZoom,
                  maxZoom: this.maxZoom
                }
              )
        
              L.tileLayer.bing({bingMapsKey: bingKey, imagerySet: 'Road'}).addTo(this.mapObject)
        
            },
            beforeDestroy () {
              this.mapObject.clearAllEventListeners()
            }
        
          }
        </script>
        

        【讨论】:

        • 此响应中没有解释指出原始发布者代码中的错误。主要是Vue中没有ready生命周期钩子,应该使用mounted
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-11-27
        • 1970-01-01
        • 2019-05-25
        • 2021-05-11
        • 2019-09-28
        • 1970-01-01
        • 2018-01-21
        相关资源
        最近更新 更多