前言

    最近在开发一款react项目,其中有一个小功能是地理位置关键词输入提示。多番考虑下,选择接入百度地图API。在使用的过程中,遇到了一些问题,在此记录并分享一下。


一、接入API

    首先,我们需要获得百度地图API的使用权,然后才能在项目中引用。

1、登录百度地图

http://lbsyun.baidu.com/

2、创建应用,获取密钥

    登录后,点击控制台,选择我的应用,点击创建应用,然后保存密钥。
在这里插入图片描述

3、引入API

    将百度API的script引入到项目的index.html文件。

<script type="text/javascript" src="http://api.map.baidu.com/api?v=2.0&ak=你的密钥"></script>

4、当作模块导入BMap

    在webpack.config.js的module.exports里添加:

externals:{
    	'BMap':'BMap',
  	}

二、使用

1、引入

    在jsx文件中引入BMap:

import BMap from 'BMap'

2、展示地图

    由于项目需要实现让用户选择某一商品的地理位置,所以需要根据用户输入的关键词给出位置提示,或者通过用户点击地图选择位置。
    这一功能作为 antd 的Form中的一项,限制比较大,但能实现。
(1)设置两个内容块放置输入框和地图
(map组件需要设置长宽)

<div id="l-map"></div>
<input type="text" id="suggestId" />

    如果直接在Form.Item中这样写,则只能显示输入框,map框会被隐藏。所以,使用help属性让<div id="l-map"></div>显示:

<Form.Item label="地址" name="address"
	help={<div id='l-map' className='map'>点我</div>}
>
	<input type="text" id="suggestId" />
</Form.Item>

(2)在组件第一次渲染后初始化地图

componentDidMount(){
	map = new BMap.Map("l-map");
    map.centerAndZoom("北京",12); // 初始化地图,设置城市和地图级别。
    map.enableScrollWheelZoom(true);//让地图能随鼠标滚轮缩放
}

    这时,界面上已经能显示输入框和地图了。
(3)添加自动完成对象

var ac = new BMap.Autocomplete(    //建立一个自动完成的对象
		{"input" : "suggestId"
		,"location" : map
	});

    将map与input绑定。这样,当在输入框输入时,输入框下方会显示此输入的位置提示信息列表。
    按照官方教程,当点击列表中的一项,输入框会呈现你的选择。然而,事与愿违,百度地图API的赋值与React不兼容。React中的Input是受控组件,不能直接id赋值。所以这里,就需要想替代办法了。
    我的想法是拦截输出并获取提示列表,然后展示列表并添加事件响应。
(4)设计输出
    首先,将其原来的输出列表隐藏:

.tangram-suggestion{
    display: none;
}

    采用一种野蛮的方法:检查元素后发现包裹列表的组件类名为“tangram-suggestion”,直接在样式中设置隐藏。

    其次,获取位置提示列表,并显示在输入框下。
    这里可以使用antd的AutoComplete组件,并且将options属性设置为组件state。只要更新state.addressOptions,就能更新选项了。(onSelect方法后文实现)

<AutoComplete id="suggestId"
	options={this.state.addressOptions}
    onSelect={this.onSelect}
/>

    拦截位置提示列表,并赋值给state.addressOptions。修改自动完成的对象如下:

new BMap.Autocomplete(    //建立一个自动完成的对象
            {
                "input" : "suggestId",
                "location" : map,
                onSearchComplete: function(e) {
        			let searchResult=[];
       				for(let i=0;i<e.Hr.length;i++){
            			let _value=e.Hr[i];
            			let a=_value.province +  _value.city +  _value.district +  _value.street +  _value.business;
            			searchResult.push({value:a});
        			}
       				this.setState({addressOptions:searchResult});
   				 }
            },
        );

    然后,实现AutoComplete的onSelect方法。onSelect方法传递的参数为列表中所选择的那一项的内容。清除地图上所有覆盖物,然后让地图中心移动到选择的位置。
(map为全局变量,后文解释)

onSelect = (data) => {
        map.clearOverlays();
        map.centerAndZoom(data,18);
    };

(5)添加地图点击响应
    当点击地图上某一位置,输入框会显示该位置,并且地图中心移动到选择的位置。
    由于点击地图只能获取经纬度信息,所以需要通过BMap.Geocoder转换为地理位置。

map.addEventListener('click', function(e){
		var pt = e.point;
        var geoc = new BMap.Geocoder();
        var addComp;
        geoc.getLocation(pt, (rs)=>{
            addComp = rs.address;
            this.uploadRef.current.setFieldsValue({address: addComp});
            map.clearOverlays();
            map.centerAndZoom(pt,18);
            map.addOverlay(new BMap.Marker(pt));
        });  
});

    对于输入框赋值,本人尝试了无数的方法,最终找到一种:setFieldsValue()赋值。this.uploadRef为Form的ref属性,地址输入框的name为address。转换后的处理函数需绑定到组件。
(6)移除事件监听
    将触发函数提取为函数:

map.addEventListener('click', this.mapClick);
mapClick=(e)=> {
        var pt = e.point;
        var geoc = new BMap.Geocoder();
        var addComp;
        geoc.getLocation(pt, (rs)=>{
            addComp = rs.address;
            this.uploadRef.current.setFieldsValue({address: addComp});
            map.clearOverlays();
            map.centerAndZoom(pt,18);
            map.addOverlay(new BMap.Marker(pt));
        });  
    }

    此时,为了能在其他函数中使用map,需要将map设置为全局变量(考虑变量作用域)。在组件外声明:

let map;

    在componentWillUnmount移除事件。

componentWillUnmount(){
	// 注销监听事件
	map.removeEventListener('click', this.mapClick);
}

(7)优化——函数防抖
    输入框输入后触发自动完成对象,是一个高频函数。为了减轻资源加载的负担,将函数延迟处理,使用debounce()。

import debounce from 'lodash/debounce';
new BMap.Autocomplete(    //建立一个自动完成的对象
    {
        "input" : "suggestId",
        "location" : map,
        onSearchComplete: this.searchComplete
    },
);
searchComplete=(e)=> {
	// console.log(e);
	let searchResult=[];
    for(let i=0;i<e.Hr.length;i++){
		let _value=e.Hr[i];
		let a=_value.province +  _value.city +  _value.district +  _value.street +  _value.business;
		searchResult.push({value:a});
	}
	this.setState({addressOptions:searchResult});
}
this.searchComplete = debounce(this.searchComplete, 500);

三、效果展示

React项目使用百度地图API效果展示.mp4


总结

    接入百度地图API这一过程完全是参考前辈们的经验,并没有遇到问题。感谢愿意分享的好心人!
    在使用过程中,遇到了形形色色的bug:变量作用域的问题、组件自带函数传递参数的问题、赋值的问题。我通常的处理方式是控制台输出,哪里不清楚就输出哪些。
    经过不断的探索,最终找到了一种方式实现了地理位置关键词输入提示这一功能。

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐