【每日一拳】打在 vue+高德地图api (@amap/amap-jsapi-loader 、vue-amap) 实践上

12,360 阅读5分钟

前言

之前也有做过地图组件(貌似使用百度 api),但大多都是cv也没有去深究。

刚好之前公司有个项目需要点击地址跳转到地图页,在地图页显示相关地址信息,也借此机会重新学习了一下高德地图 api。

找了一下相关文档感觉也不是很详细,那就写点什么记录一下吧,开干!

目前而言,对于高德地图 api 的使用看到过三种方式:

  1. 引入 @amap/amap-jsapi-loader
  2. 引入原生的高德地图 api
  3. 引入 vue-amap

@amap/amap-jsapi-loader

无意中在网上看到一个 @amap/amap-jsapi-loader 的高德地图依赖,有兴趣就去研究了一下。

npm 社区是下图这样描述的,但是 高德地图官方文档 感觉也不是很详细。

amap-jsapi-loader.jpg

所以也自己用 vue3 跑了一个 demo,小试一下。

1. 安装依赖并引入

pnpm install @amap/amap-jsapi-loader

然后在 components 下创建 Amap 组件并且引入之

import AMapLoader from "@amap/amap-jsapi-loader";

2. 初始化地图

参照 官方文档 AMapLoader.load 方法参数说明

AMapLoader.load({
    "key": "",              // 申请好的Web端开发者Key,首次调用 load 时必填
    "version": "2.0",   // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
    "plugins": []           // 需要使用的的插件列表,如比例尺'AMap.Scale'等
    "AMapUI": {             // 是否加载 AMapUI,缺省不加载
        "version": '1.1',   // AMapUI 缺省 1.1
        "plugins":[],       // 需要加载的 AMapUI ui插件
    },
    "Loca":{                // 是否加载 Loca, 缺省不加载
        "version": '1.3.2'  // Loca 版本,缺省 1.3.2
    },
}).then(()=>{
    window.AMap.xx;
    window.AMapUI.xx;
    window.Loca.xx
});

使用 AMapLoader.load 来初始化渲染地图组件,使用 AMap.Map 类创建和展示地图对象。

let map;
const mapConfigure = {
  amapKey: "", // 申请好的Web端开发者Key
  options: {
    resizeEnable: true,  // 是否监控地图容器尺寸变化
    center: [121.553958, 29.869472],  // 初始地图中心点
    zoom: 14, // 初始地图级别
  }
};

onBeforeMount(() => {
  if (!instance) return;
  let { options } = MapConfigure;

  AMapLoader.load({
    key: mapConfigure.amapKey,
    version: "2.0",
    plugins: [],
    AMapUI: {
      version: "1.1",
      plugins: []
    }
  })
    .then(AMap => {
      // 创建地图实例
      map = new AMap.Map(instance.refs.mapView, options);
    })
    .catch(() => {
      throw "地图加载失败,请重新加载";
    });
});
<template>
  <div id="mapview" ref="mapview"></div>
</template>

初始化地图.jpg

3. 使用 SimpleMarker 创建标记点

SimpleMarker(简单标注)继承自 AMap.Marker,在已有功能的基础上,额外增加如下的支持:

  • 支持设置背景图标(iconThemeiconStyle)和前景文字(iconLabel);背景图标内置若干样式可供挑选(如上方示例),也支持自定义图片地址或者Dom结构。
  • 支持显示定位点,默认用红点标识(查看示例),红点的中心即是经纬度(即position)对应的位置。用于开发阶段,辅助开发者设置Marker图标相对于经纬度的显示偏移量。(即Marker的offset参数)
  1. 加载 SimpleMarker(模块名:ui/overlay/SimpleMarker

    AMapLoader.load({
      key: mapConfigure.amapKey,
      version: "2.0",
      AMapUI: {
        version: "1.1",
        plugins: ["overlay/SimpleMarker"]  // 需要加载的 AMapUI ui插件
      }
    })
    
  2. 创建 SimpleMarker 实例

    AMapLoader.load({
      key: mapConfigure.amapKey,
      version: "2.0",
      AMapUI: {
        version: "1.1",
        plugins: ["overlay/SimpleMarker"]
      }
    }).then((AMap) => {
        map = new AMap.Map(instance.refs.mapView, options);
    
        // !!! 通过 AMap.SimpleMarker 获取组件
        new AMapUI.SimpleMarker({
            //前景文字
            iconLabel: 'A',
            //图标主题
            iconTheme: 'default',
            //背景图标样式
            iconStyle: 'red',
            map: map,
            position: map.getCenter()
        });
    }).catch((e) => {
        throw "地图加载失败,请重新加载";
    });
    

marker1.jpg

  1. 可以更改标记点 marker 的样式,参考官方文档:lbs.amap.com/api/amap-ui…

    AMapLoader.load({
      key: mapConfigure.amapKey,
      version: "2.0",
      AMapUI: {
        version: "1.1",
        plugins: ["overlay/SimpleMarker"]
      }
    }).then((AMap) => {
        map = new AMap.Map(instance.refs.mapView, options);
    
        let marker = new AMapUI.SimpleMarker({
            // 前景文字
            // iconLabel: "天一",
            // 自定义图标地址
            iconStyle: {
              src: "http://webapi.amap.com/theme/v1.3/markers/b/mark_r.png",
              style: {
                width: "20px",
                height: "30px"
              }
            },
            // 设置基点偏移
            // offset: new AMap.Pixel(0, -30),
            // showPositionPoint: true,
            map: map,
            position: map.getCenter(),
          });
    }).catch((e) => {
        throw "地图加载失败,请重新加载";
    });
    

marker2.jpg

4. 使用 SimpleInfoWindow 创建信息窗体

SimpleInfoWindow(简单信息窗体)继承自 AMap.InfoWindow,提供一种简单的“标题+内容”构造的信息窗体;内容的构建支持使用模板。

  1. 加载 SimpleInfoWindow(模块名:ui/overlay/SimpleInfoWindow

    AMapLoader.load({
      key: mapConfigure.amapKey,
      version: "2.0",
      AMapUI: {
        version: "1.1",
        plugins: ["overlay/SimpleInfoWindow"]  // 需要加载的 AMapUI ui插件
      }
    })
    
  2. 创建 SimpleInfoWindow 实例,参考官方文档:lbs.amap.com/api/amap-ui…

    AMapLoader.load({
      key: mapConfigure.amapKey,
      version: "2.0",
      AMapUI: {
        version: "1.1",
        plugins: ["overlay/SimpleInfoWindow"]
      }
    }).then(AMap => {
      …………
      
      let infoWindow = new AMapUI.SimpleInfoWindow({
        infoTitle: '<strong>这里是标题</strong>',  // 标题内容,html代码
        infoBody: '<p>这里是内容。</p>',  //  主体内容,html代码
      });
      infoWindow.open(map, map.getCenter());
    
      map.add(infoWindow); // 添加图层到地图
    })
    .catch(() => {
      throw "地图加载失败,请重新加载";
    });
    
  3. 自定义窗体内容

    import square from "../../assets/images/square.jpeg";
    
    const info = {
      name: "天一广场",
      address: "浙江省宁波市海曙区中山东路188号",
      phone: "(0574)87683088",
      time: "10:00-22:00",
      imageUrl: square
    };
    
    AMapLoader.load({
      key: mapConfigure.amapKey,
      version: "2.0",
      AMapUI: {
        version: "1.1",
        plugins: ["overlay/SimpleMarker", "overlay/SimpleInfoWindow"]
      }
    }).then(AMap => {
      …………
      
      let infoWindow = new AMapUI.SimpleInfoWindow({
        infoTitle: `<span style="font-size: 14px;">${info.name}</span>`,
        infoBody: instance.refs.infoWindow.innerHTML,
        offset: new AMap.Pixel(0, -40),
        // position: map.getCenter(),
      });
      infoWindow.open(map, map.getCenter());
    
      map.add(infoWindow); // 添加图层到地图
    })
    .catch(() => {
      throw "地图加载失败,请重新加载";
    });
    
    <template>
      <div id="mapView" ref="mapView">
        <!--自定义窗体-->
        <div class="info" ref="infoWindow">
          <!-- <div class="info-head">{{ info.name }}</div> -->
          <div style="display: flex; align-items: center">
            <div style="width: 120px; height: 80px; margin-right: 10px">
              <img
                style="width: 100%; height: 100%"
                :src="info.imageUrl"
                :alt="info.name"
              />
            </div>
            <div style="font-size: 13px">
              <p>地址:{{ info.address }}</p>
              <p>电话:{{ info.phone }}</p>
              <p>营业时间: {{ info.time }}</p>
            </div>
          </div>
        </div>
      </div>
    </template>
    

infoWindow.jpg

  1. 给 marker 添加点击事件,也可以给窗体内容绑定监听事件,参考文档:lbs.amap.com/demo/amap-u…

    AMapLoader.load({
      key: mapConfigure.amapKey,
      version: "2.0",
      AMapUI: {
        version: "1.1",
        plugins: ["overlay/SimpleMarker", "overlay/SimpleInfoWindow"]
      }
    }).then(AMap => {
      …………
      
      const openInfoWin = () => {
        infoWindow.open(map, map.getCenter());
      };
    
      // marker 点击时打开
      marker.on("click", function () {
        openInfoWin();
      });
    
      map.add(infoWindow); // 添加图层到地图
    })
    .catch(() => {
      throw "地图加载失败,请重新加载";
    });
    

5. 添加地图控件

在线插件是在基础地图服务上增加的额外功能,您可以根据自己的需要选择添加。插件分为两类:一类是地图控件,它们是用户与地图交互的UI元素,例如缩放控制条(ToolBar)等;一类是功能型插件,用来完成某些特定地图功能,比如鼠标工具(MouseTool)等。

控件名称说明是否插件
AMap.ControlBar组合了旋转、倾斜、复位、缩放在内的地图控件,在3D地图模式下会显示(自V1.4.0版本新增)
AMap.MapType地图类型切换插件,用来切换固定的几个常用图层
AMap.OverView地图鹰眼插件,默认在地图右下角显示缩略图
AMap.Scale地图比例尺插件
AMap.ToolBar地图工具条插件,可以用来控制地图的缩放和平移

可以参考官方文档:lbs.amap.com/api/javascr…

AMapLoader.load({
  key: mapConfigure.amapKey,
  version: "2.0",
  plugins: ["AMap.MarkerCluster"],
  AMapUI: {
    version: "1.1",
    plugins: ["overlay/SimpleMarker", "overlay/SimpleInfoWindow"]
  }
}).then(AMap => {
    …………

    map.plugin(
      [
        "AMap.ToolBar",
        "AMap.MapType",
        "AMap.Geolocation",
        "AMap.Scale",
        "AMap.ControlBar"
      ],
      // [],
      () => {
        // 地图工具条插件,可以用来控制地图的缩放和平移
        map.addControl(new AMap.ToolBar());
        // 地图类型切换插件,用来切换固定的几个常用图层
        map.addControl(
          new AMap.MapType({
            defaultType: 0
          })
        );
        // 比例尺插件, 位于地图左下角
        map.addControl(new AMap.Scale());
        // map.addControl(new AMap.Geolocation());
        map.addControl(new AMap.ControlBar());
      }
    );

  	…………
  })
  .catch(() => {
    throw "地图加载失败,请重新加载";
  });
});

地图控件.jpg

6. 选址组件

可以查看我的另一篇文章 《vue+高德地图api 封装选址组件》

7. 其他功能

  • PointSimplifier:是一个针对海量点展示场景的组件,能够支持较大规模的经纬度数据,以及配置丰富的展示效果。
  • MarkerCluster:使用 AMap.MarkerCluster 展示大量点标记,可灵活设定聚合样式。
  • ……

暂时没有去深究,以后有时间补上,通通补上。

8. 完整代码

<script setup lang="ts">
import AMapLoader from "@amap/amap-jsapi-loader";
import { getCurrentInstance, onBeforeMount, onUnmounted } from "vue";
import square from "../../assets/images/square.jpeg";

interface MapConfigOption {
  resizeEnable?: boolean;
  center?: number[];
  zoom?: number;
}

interface MapConfigure {
  amapKey: string;
  options: MapConfigOption;
}

interface MapConfigureInter {
  on: Fn;
  destroy?: Fn;
  clearEvents?: Fn;
  addControl?: Fn;
  getCenter?: Fn;
  setCenter?: Fn;
  setZoom?: Fn;
  plugin?: Fn;
}

let map: MapConfigureInter;

const mapConfigure: MapConfigure = {
  amapKey: "b445fc42ae1593929170feca71b3e3b9",
  options: {
    resizeEnable: true,
    center: [121.553958, 29.869472],
    zoom: 14
  }
};

const instance = getCurrentInstance();

const info = {
  name: "天一广场",
  address: "浙江省宁波市海曙区中山东路188号",
  phone: "(0574)87683088",
  time: "10:00-22:00",
  imageUrl: square
};

onBeforeMount(() => {
  if (!instance) return;
  let { options } = mapConfigure;

  AMapLoader.load({
    key: mapConfigure.amapKey,
    version: "2.0",
    plugins: ["AMap.MarkerCluster"],
    AMapUI: {
      version: "1.1",
      plugins: ["overlay/SimpleMarker", "overlay/SimpleInfoWindow"]
    }
  })
    .then(AMap => {
      // 创建地图实例
      map = new AMap.Map(instance.refs.mapView, options);

      // 显示标记点
      let marker = new AMapUI.SimpleMarker({
        //前景文字
        // iconLabel: "天一广场",
        //自定义图标地址
        iconStyle: {
          src: "http://webapi.amap.com/theme/v1.3/markers/b/mark_r.png",
          style: {
            width: "20px",
            height: "30px"
          }
        },
        // iconStyle: "lightblue",
        //设置基点偏移
        // offset: new AMap.Pixel(0, -30),
        // showPositionPoint: true,
        map: map,
        position: map.getCenter()
      });

      // 地图中添加地图操作ToolBar插件
      map.plugin(
        [
          "AMap.ToolBar",
          "AMap.MapType",
          "AMap.Geolocation",
          "AMap.Scale",
          "AMap.ControlBar"
        ],
        // [],
        () => {
          // 地图工具条插件,可以用来控制地图的缩放和平移
          map.addControl(new AMap.ToolBar());
          // 地图类型切换插件,用来切换固定的几个常用图层
          map.addControl(
            new AMap.MapType({
              defaultType: 0
            })
          );
          // 比例尺插件, 位于地图左下角
          map.addControl(new AMap.Scale());
          // map.addControl(new AMap.Geolocation());
          map.addControl(new AMap.ControlBar());
        }
      );

      let infoWindow = new AMapUI.SimpleInfoWindow({
        infoTitle: `<span style="font-size: 14px;">${info.name}</span>`,
        infoBody: instance.refs.infoWindow.innerHTML,
        offset: new AMap.Pixel(0, -40)
        // position: map.getCenter(),
      });
      infoWindow.open(map, map.getCenter());

      const openInfoWin = () => {
        infoWindow.open(map, map.getCenter());
      };

      // marker 点击时打开
      marker.on("click", function () {
        openInfoWin();
      });

      map.add(infoWindow); // 添加图层到地图
    })
    .catch(() => {
      throw "地图加载失败,请重新加载";
    });
});

onUnmounted(() => {
  if (map) {
    // 销毁地图实例
    map.destroy() && map.clearEvents("click");
  }
});
</script>

<template>
  <div id="mapView" ref="mapView">
    <!--自定义窗体-->
    <div class="info" ref="infoWindow">
      <!-- <div class="info-head">{{ info.name }}</div> -->
      <div style="display: flex; align-items: center">
        <div style="width: 120px; height: 80px; margin-right: 10px">
          <img
            style="width: 100%; height: 100%"
            :src="info.imageUrl"
            :alt="info.name"
          />
        </div>
        <div style="font-size: 13px">
          <p>地址:{{ info.address }}</p>
          <p>电话:{{ info.phone }}</p>
          <p>营业时间: {{ info.time }}</p>
        </div>
      </div>
    </div>
  </div>
</template>

<style lang="scss" scoped>
#mapView {
  height: calc(100vh - 86px);
}

.info {
  display: none;
}

:deep(.amap-marker-label) {
  border: none !important;
}
</style>

引入原生的高德 api

1. vue2 + 移动端

1.1 初始化地图

在 components 下创建 Amap 组件,使用 <script> 标签导入高德地图 api。

<template>
  <div id="mapview" ref="mapview"></div>
</template>

<script>
import { mapState } from "vuex";
    
let map = null;
let infoWindow = null;

export default {
  name: "map-view",
  computed: {
    ...mapState(["info"]),
  },
  mounted() {
    const amap_key = '', cb = "amap_callback";
    const scriptUrl = `https://webapi.amap.com/maps?v=1.4.18&key=${amap_key}&callback=${cb}`;

    // 导入script
    importScript(scriptUrl);

    window[cb] = () => {
      // 初始化地图
      this.initMap();
    };
  },
  methods: {
    initMap() {
      const info = this.info;
      const position = [info.longitude, info.latitude];

      map = new AMap.Map("map-view", {
        resizeEnable: true,
        zoom: 15,
        center: position,
      });
        
      AMap.plugin(
        ["AMap.ToolBar", "AMap.Geolocation", "AMap.Scale"],
        function () {
          map.addControl(new AMap.ToolBar());
          map.addControl(new AMap.Scale());
          map.addControl(new AMap.Geolocation());
        }
      );
    },
  }
};
</script>

1.2 添加 marker 标记和信息窗口

<template>
  <div id="mapView" ref="mapView">
    <!--自定义窗体-->
    <div class="info" ref="infoWindow">
      <div class="info-head">
        <span class="info-title">{{ info.name }}</span>
        <span
          class="info-close iconfont icon-guanbi"
          @click="closeHandler"
        ></span>
      </div>
      <div class="info-body">
        <div class="info-img-wrap">
          <img :src="info.imageUrl" :alt="info.name" class="info-img" />
        </div>
        <div>
          <p>地址:{{ info.address | defaultInfo }}</p>
          <p>电话:{{ info.phone | defaultInfo }}</p>
          <p>营业时间: {{ info.time | defaultInfo }}</p>
        </div>
      </div>
    </div>
  </div>
</template>
methods: {
  initMap() {
	…………
    
    let marker = new AMap.Marker({
      title: info.address,
      position,
      map
    });

    // 自定义窗体
    infoWindow = new AMap.InfoWindow({
      isCustom: true,
      content: this.$refs["infoWindow"],
      offset: new AMap.Pixel(0, -50),
    });
    infoWindow.open(map, position);

    let closeBtn = document.querySelector(".info-close");
    marker.on("click", this.closeHandler);
  },
  closeHandler() {
    const info = this.info;
    const position = [info.longitude, info.latitude];

    if (infoWindow.getIsOpen()) {
      infoWindow.close();
      return;
    }
    infoWindow.open(map, position);
  },
}
<style lang="scss" scoped>
#mapView {
  position: relative;
  height: 100%;
  width: 100%;
  min-height: 500px;

  .info {
    position: relative;
    border: solid 1px silver;
    max-width: 270px;

    &-head {
      display: flex;
      position: relative;
      background: #f9f9f9;
      height: 30px;
      border-bottom: 0.5px solid #ccc;
      border-radius: 5px 5px 0 0;
      padding: 0 5px;
      align-items: center;
      justify-content: space-between;

      .info-title {
        flex: 1;
        max-width: 200px;
        font-size: 14px;
        font-weight: 500;
        overflow: hidden;
        white-space: nowrap;
        text-overflow: ellipsis;
      }
    }

    &-body {
      display: flex;
      background: #ffffff;
      font-size: 12px;
      padding: 6px;
      line-height: 20px;

      .info-img-wrap {
        width: 100px;
        height: 80px;
        margin-right: 5px;
        flex-shrink: 0;

        .info-img {
          width: 100%;
          height: 100%;
        }
      }

      p {
        word-break: break-all;
      }
    }
  }
}
</style>

1.3 完整代码

<template>
  <div id="mapView" ref="mapView">
    <!--自定义窗体-->
    <div class="info" ref="infoWindow">
      <div class="info-head">
        <span class="info-title">{{ info.name }}</span>
        <span
          class="info-close iconfont icon-guanbi"
          @click="closeHandler"
        ></span>
      </div>
      <div class="info-body">
        <div class="info-img-wrap">
          <img :src="info.imageUrl" :alt="info.name" class="info-img" />
        </div>
        <div>
          <p>地址:{{ info.address | defaultInfo }}</p>
          <p>电话:{{ info.phone | defaultInfo }}</p>
          <p>营业时间: {{ info.time | defaultInfo }}</p>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import square from "../assets/img/square.jpeg";
import { mapState } from "vuex";

let infoWindow = null;
let map = null;

export default {
  name: "map-view",
  computed: {
    ...mapState(["info"]),
  },
  mounted() {
    const amap_key = '', cb = "amap_callback";
    const scriptUrl = `https://webapi.amap.com/maps?v=1.4.18&key=${amap_key}&callback=${cb}`;

    // 导入script
    importScript(scriptUrl);

    window[cb] = () => {
      // 初始化地图
      this.initMap();
    };
  },
  methods: {
    initMap() {
      const info = this.info;
      const position = [info.longitude, info.latitude];

      map = new AMap.Map("mapView", {
        resizeEnable: true,
        zoom: 15,
        center: position,
      });
        
      AMap.plugin(
        ["AMap.ToolBar", "AMap.Geolocation", "AMap.Scale"],
        function () {
          map.addControl(new AMap.ToolBar());
          map.addControl(new AMap.Scale());
          map.addControl(new AMap.Geolocation());
        }
      );

      // 自定义窗体
      infoWindow = new AMap.InfoWindow({
        isCustom: true,
        content: this.$refs["infoWindow"],
        offset: new AMap.Pixel(0, -50),
      });
      infoWindow.open(map, arr);

      let closeBtn = document.querySelector(".info-close");
      marker.on("click", this.closeHandler);
    },
    closeHandler() {
      const info = this.info;
      const position = [info.longitude, info.latitude];

      if (infoWindow.getIsOpen()) {
        infoWindow.close();
        return;
      }
      infoWindow.open(map, position);
    },
  },
};

function importScript(sSrc, success) {
  function loadError(err) {
    throw new URIError("The script " + err.target.src + " is not accessible.");
  }

  var oScript = document.createElement("script");
  oScript.type = "text\/javascript";
  oScript.onerror = loadError;
  if (success) oScript.onload = success;
  document.body.appendChild(oScript);
  oScript.src = sSrc;
}
</script>

<style lang="scss" scoped>
#mapView {
  position: relative;
  height: 100%;
  width: 100%;
  min-height: 500px;

  .info {
    position: relative;
    border: solid 1px silver;
    max-width: 270px;

    &-head {
      display: flex;
      position: relative;
      background: #f9f9f9;
      height: 30px;
      border-bottom: 0.5px solid #ccc;
      border-radius: 5px 5px 0 0;
      padding: 0 5px;
      align-items: center;
      justify-content: space-between;

      .info-title {
        flex: 1;
        max-width: 200px;
        font-size: 14px;
        font-weight: 500;
        overflow: hidden;
        white-space: nowrap;
        text-overflow: ellipsis;
      }
    }

    &-body {
      display: flex;
      background: #ffffff;
      font-size: 12px;
      padding: 6px;
      line-height: 20px;

      .info-img-wrap {
        width: 100px;
        height: 80px;
        margin-right: 5px;
        flex-shrink: 0;

        .info-img {
          width: 100%;
          height: 100%;
        }
      }

      p {
        word-break: break-all;
      }
    }
  }
}
</style>

引入原生的高德api.jpg

2. vue3 + pc端

这个本人没有去深入研究,在 githug 一个项目上看到过,都是导入 script 标签,大致效果也是一样的,也顺便贴一下代码,有兴趣的可以自己去研究哈。

  1. 组件代码:

    <template>
      <div ref="wrapRef" :style="{ height, width }"></div>
    </template>
    <script lang="ts">
      import { defineComponent, ref, nextTick, unref, onMounted } from 'vue';
    
      import { useScript } from '/@/hooks/web/useScript';
    
      const A_MAP_URL = 'https://webapi.amap.com/maps?v=2.0&key=申请好的Web端开发者Key';
    
      export default defineComponent({
        name: 'AMap',
        props: {
          width: {
            type: String,
            default: '100%',
          },
          height: {
            type: String,
            default: 'calc(100vh - 78px)',
          },
        },
        setup() {
          const wrapRef = ref<HTMLDivElement | null>(null);
          const { toPromise } = useScript({ src: A_MAP_URL });
    
          async function initMap() {
            await toPromise();
            await nextTick();
            const wrapEl = unref(wrapRef);
            if (!wrapEl) return;
            const AMap = (window as any).AMap;
            new AMap.Map(wrapEl, {
              zoom: 11,
              center: [116.397428, 39.90923],
              viewMode: '3D',
            });
          }
    
          onMounted(() => {
            initMap();
          });
    
          return { wrapRef };
        },
      });
    </script>
    
  2. hooks 文件:

    import { onMounted, onUnmounted, ref } from 'vue';
    
    interface ScriptOptions {
      src: string;
    }
    
    export function useScript(opts: ScriptOptions) {
      const isLoading = ref(false);
      const error = ref(false);
      const success = ref(false);
      let script: HTMLScriptElement;
    
      const promise = new Promise((resolve, reject) => {
        onMounted(() => {
          script = document.createElement('script');
          script.type = 'text/javascript';
          script.onload = function () {
            isLoading.value = false;
            success.value = true;
            error.value = false;
            resolve('');
          };
    
          script.onerror = function (err) {
            isLoading.value = false;
            success.value = false;
            error.value = true;
            reject(err);
          };
    
          script.src = opts.src;
          document.head.appendChild(script);
        });
      });
    
      onUnmounted(() => {
        script && script.remove();
      });
    
      return {
        isLoading,
        error,
        success,
        toPromise: () => promise,
      };
    }
    

vue-amap

另一种使用 vue-amap 这个依赖的也人较多,但是 npm 社区最近的一次更新已经是三年前的事情了,而且 vue-amap 是一套基于 Vue 2.0 和高德地图的地图组件,如果使用 vue3 开发这个依赖就已经不适用了。

1. 安装依赖

首先在 main.js 引入依赖,然后使用 Vue.use 调用,并初始化 vue-amap

// 高德地图
import Amap from "vue-amap";
Vue.use(Amap);

Amap.initAMapApiLoader({
  key: "",  // 申请好的Web端开发者Key
  plugin: [   // 插件集合
    "AMap.Autocomplete",
    "AMap.PlaceSearch",
    "AMap.Scale",
    "AMap.OverView",
    "AMap.ToolBar",
    "AMap.MapType",
    "AMap.PolyEditor",
    "AMap.CircleEditor",
  ],
  // 默认高德 sdk 版本为 1.4.4
  v: "1.4.4",
});

2. 引用组件

<el-amap
  class="amap-box"
  :vid="'amap-vue'"
  :zoom="zoom"
  :center="center"
>
  <el-amap-marker :position="center"></el-amap-marker>
</el-amap>

vue-amap地图.jpg


写在最后

无论什么方法,只要可以实现最终的诉求都是一个好方法,都值得一试。

接下来我也会继续去深究更多的东西,向大佬们看齐,也希望大家喜欢。

likeNum++;

readingQuantity++;

加油!