在 Vue3 项目里接高德地图,建议直接做成“脚本加载 + 公共方法封装”的方式,后续多个页面复用更省事。常见需求基本就是:地图初始化、Marker 标记、定位、地图选点、逆地理编码、地址解析、路线规划。

先封装地图脚本加载,避免重复引入,也避免 `AMap is not defined`:

// src/utils/loadAMap.js
let amapPromise = null

export function loadAMap() {
  // 如果已加载则直接返回
  if (window.AMap) return Promise.resolve(window.AMap)
  
  // 避免重复加载
  if (amapPromise) return amapPromise

  amapPromise = new Promise((resolve, reject) => {
    const script = document.createElement('script')
    script.src = 'https://webapi.amap.com/maps?v=2.0&key=***&plugin=AMap.Geolocation,AMap.Geocoder,AMap.Driving'
    script.async = true
    
    script.onload = () => resolve(window.AMap)
    script.onerror = () => reject(new Error('高德地图加载失败'))
    
    document.head.appendChild(script)
  })

  return amapPromise
}
 

页面里最小初始化写法:


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

<script setup>
import { onMounted, onBeforeUnmount } from 'vue'
import { loadAMap } from '@/utils/loadAMap'

let map = null
let AMapRef = null

async function initMap() {
  const AMap = await loadAMap()
  AMapRef = AMap

  map = new AMap.Map('map-container', {
    zoom: 12,
    center: [116.397428, 39.90923],
    resizeEnable: true
  })
}

onMounted(initMap)

onBeforeUnmount(() => {
  if (map) {
    map.destroy()
    map = null
  }
})
</script>

<style scoped>
#map-container {
  width: 100%;
  height: 500px;
}
</style>

常用功能里,Marker 和信息窗体最常见:


function addMarker() {
  const marker = new AMapRef.Marker({
    position: [116.397428, 39.90923],
    title: '天安门',
    map
  })

  const infoWindow = new AMapRef.InfoWindow({
    content: '<div>天安门</div>',
    offset: new AMapRef.Pixel(0, -30)
  })

  marker.on('click', () => {
    infoWindow.open(map, marker.getPosition())
  })
}

定位和逆地理编码一般一起用:


function getCurrentPosition() {
  map.plugin('AMap.Geolocation', () => {
    const geolocation = new AMapRef.Geolocation({
      enableHighAccuracy: true,
      timeout: 10000
    })

    geolocation.getCurrentPosition((status, result) => {
      if (status === 'complete') {
        const { lng, lat } = result.position
        map.setCenter([lng, lat])

        new AMapRef.Marker({
          position: [lng, lat],
          map
        })

        const geocoder = new AMapRef.Geocoder()
        geocoder.getAddress([lng, lat], (s, r) => {
          if (s === 'complete' && r.info === 'OK') {
            console.log(r.regeocode.formattedAddress)
          }
        })
      }
    })
  })
}

地图选点也很实用,适合门店选址、地址回填:

let currentMarker = null

function bindMapClick() {
  map.on('click', e => {
    const { lng, lat } = e.lnglat

    if (currentMarker) {
      map.remove(currentMarker)
    }

    currentMarker = new AMapRef.Marker({
      position: [lng, lat],
      map
    })

    map.setCenter([lng, lat])
  })
}

如果要做地址转坐标、路线规划,也建议直接封装:


function geocodeAddress(address) {
  const geocoder = new AMapRef.Geocoder()
  geocoder.getLocation(address, (status, result) => {
    if (status === 'complete' && result.info === 'OK') {
      console.log(result.geocodes)
    }
  })
}

function planRoute(start, end) {
  map.plugin('AMap.Driving', () => {
    const driving = new AMapRef.Driving({ map })
    driving.search(start, end)
  })
}

最后记住几个高频坑就够了:

- 地图不显示,先查容器高度
- `AMap is not defined`,基本都是脚本没加载完
- 经纬度顺序固定是 `[lng, lat]`
- 定位失败先查浏览器权限
- 页面切换后异常,记得销毁地图实例

这套写法适合大多数 Vue3 项目。

更多推荐