react如何引用(按需加载)百度地图,并结合and组件化封装

1.技术选项:

vite+react+antdesign

load-script

2.实现思路:

1.按需加载如何实现?

  要实现按需加载就不能直接在项目的入口文件这种地方去通过script标签引入,这里使用load-script封装了一个加载百度地图的Bmap.js方法,实现动态的插入script脚本。

根目录下创建Bmap.js文件

react如何引用(按需加载)百度地图,并结合and组件化封装_第1张图片

import _loadScript from 'load-script';
 
export function Map(url) {
    return new Promise((resolve, reject) => {
        _loadScript(url, (error, script) => {
            if (error) {
                return reject(error);
            }
            console.log('====================================');
            console.log(script);
            console.log('====================================');
            resolve(script);
        });
    });
}
 
 
export default function loadBMap() {
    return new Promise(function(resolve, reject) {
        if (typeof BMap !== 'undefined') {
            resolve(BMap);
            return true;
        }
        window.onBMapCallback = function() {
            resolve(BMap);
        };
        let script = document.createElement('script');
        script.type = 'text/javascript';
        script.src =
            'https://api.map.baidu.com/api?v=3.0&ak=自己的';
        script.onerror = reject;
        document.head.appendChild(script);
    });
}
2.初始化加载地图(注意事项)

要初始化加载地图,我们需要确保的时候,在页面绘制完,或者弹框加载完成之后再去调用初始化的方法,否则就会报错。

报错示例:

Cannot read properties of undefined (reading  kc )

封装示例图

react如何引用(按需加载)百度地图,并结合and组件化封装_第2张图片

3.map.js脚本实现如下:

// 加载百度地图
export function LoadBaiduMapScript() {
  console.log("百度地图脚本初始化ing----------------");
  const BMap_URL =
    "https://api.map.baidu.com/api?v=3.0&ak=换成自己的&callback=onBMapCallback";
  return new Promise((resolve, _reject) => {
    // 如果已加载直接返回
    if (typeof BMap !== "undefined") {
      resolve(BMap);
      return true;
    }
    // 百度地图异步加载回调处理
    window.onBMapCallback = function () {
      console.log("百度地图脚本初始化成功...");
      resolve(BMap);
    };
    // 插入script脚本
    const scriptNode = document.createElement("script");
    scriptNode.setAttribute("type", "text/javascript");
    scriptNode.setAttribute("src", BMap_URL);
    document.body.appendChild(scriptNode);
  });
}

React实现代码如下:

注意:new BMap 可能会爆红 不用管,不影响使用

地图实例,标记示例,手掌选择实例使用useRef存储吗,使用useState存储会导致内容重新渲染,造成初始化失败

import { Modal, Button, message, Input, Spin } from "antd";
import {
  useState,
  forwardRef,
  useImperativeHandle,
  useEffect,
  useRef,
} from "react";
import positionIcon from "@/assets/images/position.png";
import { LoadBaiduMapScript } from "./map";
import "./index.less";

interface MapPropsType {
  onConfirm: (val) => void;
}

const MapChoice = forwardRef((props: MapPropsType, ref) => {
  const { Search } = Input;
  const [isModalOpen, setIsModalOpen] = useState(false);
  const [searchValue, setSearchValue] = useState("");
  const [loading, setLoading] = useState(false);
  const [address, setAddress] =
    useState("河南省郑州市二七区建设东路48号");
  const [lon, setLon] = useState(113.65);
  const [lat, setLat] = useState(34.76);

  // 使用 ref 存储地图相关对象(避免状态异步问题)
  const mapRef = useRef(null);
  const pointRef = useRef(null);
  const markerRef = useRef(null);

  const showModal = () => {
    setIsModalOpen(true);
  };

  const handleOk = () => {
    setIsModalOpen(false);
    const data = { address, lon, lat };
    props.onConfirm(data); // 回调方式
  };

  const handleCancel = () => {
    setIsModalOpen(false);
  };

  useImperativeHandle(ref, () => ({
    showModal,
    handleOk,
    handleCancel,
  }));

  useEffect(() => {
    if (!isModalOpen) return;

    const init = async () => {
      await LoadBaiduMapScript();
      initMap({});
      await browserPosition();
    };

    init();
  }, [isModalOpen]);

  // 初始化地图
  const initMap = (record: any) => {
    const currentAddress = record.address || address || "郑州市";
    const currentLon = record?.coordinates?.lon ?? lon;
    const currentLat = record?.coordinates?.lat ?? lat;

    // 创建地图实例(使用 ref 存储)
    const mapInstance = new BMap.Map("container");
    const pointInstance = new BMap.Point(currentLon, currentLat);
    const myIcon = new BMap.Icon(positionIcon, new BMap.Size(23, 25));
    const markerInstance = new BMap.Marker(pointInstance, myIcon);

    // 存储到 ref
    mapRef.current = mapInstance;
    pointRef.current = pointInstance;
    markerRef.current = markerInstance;

    // 初始化地图设置
    mapInstance.centerAndZoom(pointInstance, 15);
    mapInstance.enableScrollWheelZoom();
    mapInstance.addOverlay(markerInstance);

    // 更新状态
    setAddress(currentAddress);
    setLon(currentLon);
    setLat(currentLat);

    // 绑定点击事件
    mapInstance.addEventListener("click", (e: any) => {
      const clickedPoint = new BMap.Point(e.point.lng, e.point.lat);
      mapInstance.centerAndZoom(clickedPoint, 15);
      pointRef.current = clickedPoint;

      // 逆地理编码获取地址
      const gc = new BMap.Geocoder();
      gc.getLocation(clickedPoint, (rs: any) => {
        if (rs?.address) {
          setAddress(rs.address);
          setLon(e.point.lng);
          setLat(e.point.lat);
          upInfoWindow(rs.address, e.point.lng, e.point.lat);
        }
      });
    });

    // 初始信息窗口
    upInfoWindow(currentAddress, currentLon, currentLat);
  };

  // 更新信息窗口
  const upInfoWindow = (address: string, lon: number, lat: number) => {
    if (!mapRef.current || !pointRef.current) return;

    const opts = {
      width: 250,
      height: 120,
      title: "经纬度",
    };
    const word = `
地址:${address}
经度:${lon}
纬度:${lat}
`; const infoWindow = new BMap.InfoWindow(word, opts); mapRef.current.openInfoWindow(infoWindow, pointRef.current); markerRef.current.addEventListener("click", () => { mapRef.current.openInfoWindow(infoWindow, pointRef.current); }); }; // 地址搜索 const handleSearch = () => { if (!searchValue?.trim()) { message.warning("搜索框不能为空"); return; } const myGeo = new BMap.Geocoder(); myGeo.getPoint(searchValue.trim(), (point: any) => { if (point) { mapRef.current.centerAndZoom(point, 15); pointRef.current = point; setLon(point.lng); setLat(point.lat); const gc = new BMap.Geocoder(); gc.getLocation(point, (rs: any) => { if (rs?.address) { setAddress(rs.address); upInfoWindow(rs.address, point.lng, point.lat); } }); } else { message.warning("您选择的地址没有解析到结果!"); } }); }; // 浏览器定位 const browserPosition = async () => { setLoading(true); const geolocation = new BMap.Geolocation(); geolocation.getCurrentPosition(async (r: any) => { if (r?.point) { const point = new BMap.Point(r.point.lng, r.point.lat); mapRef.current.centerAndZoom(point, 15); pointRef.current = point; const gc = new BMap.Geocoder(); gc.getLocation(point, (rs: any) => { if (rs?.address) { setAddress(rs.address); setLon(r.point.lng); setLat(r.point.lat); upInfoWindow(rs.address, r.point.lng, r.point.lat); } }); } else { message.error("定位失败,请手动输入经纬度"); } setLoading(false); }); }; return ( <>
位置
setSearchValue(e.target.value)} onSearch={handleSearch} style={{ width: "40%" }} />
); }); export default MapChoice;

less样式代码

.positionbox {
    width: 100%;
    height: 64vh;
    margin-top: 20px;
  }

实现效果如下

react如何引用(按需加载)百度地图,并结合and组件化封装_第3张图片

你可能感兴趣的:(react.js,前端,前端框架)