news 2026/4/28 17:19:34

别再自己爬数据了!用这个免费API快速搞定省市区三级联动选择器(附前端Vue/React代码)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
别再自己爬数据了!用这个免费API快速搞定省市区三级联动选择器(附前端Vue/React代码)

省市区三级联动选择器:免费API与前端框架实战指南

每次开发表单系统时,最头疼的就是省市区选择器的数据维护问题。手动维护不仅耗时耗力,还要应对行政区划的频繁调整。本文将介绍如何利用免费API快速构建动态加载的三级联动选择器,并提供Vue和React两种主流框架的完整实现方案。

1. 为什么需要动态行政区划API

传统的前端省市区选择器通常采用静态JSON数据,这种方式存在几个明显缺陷:

  • 数据更新滞后:行政区划每年都有调整(如撤县设区、新设地级市等),静态数据需要手动更新
  • 体积臃肿:完整的省市区数据JSON文件可能达到几百KB,影响首屏加载速度
  • 维护成本高:需要定期检查数据准确性,跨项目复用困难

相比之下,动态API方案具有以下优势:

对比维度静态JSON方案动态API方案
数据时效性依赖手动更新实时最新
网络传输一次性加载全部数据按需加载
维护成本
跨项目复用需要复制文件直接调用接口

adcode(行政区划代码)是这个系统的关键,它是国家标准的行政区划唯一标识,由6位数字组成:

  • 前2位:省/直辖市代码
  • 中间2位:地级市代码
  • 后2位:区县代码

2. 免费行政区划API详解

我们推荐使用高德地图开放平台的行政区划查询API,它具有以下特点:

  • 完全免费(每日限额足够一般应用使用)
  • 数据权威,与国家统计局同步更新
  • 无需注册小程序或签到获取密钥

2.1 API基础配置

const API_URL = 'https://restapi.amap.com/v3/config/district' const API_KEY = '您申请的高德开发者key' // 建议存储在环境变量中 // 基础请求参数 const baseParams = { key: API_KEY, extensions: 'all', // 获取全部子级 subdistrict: 3, // 递归获取三级行政区 }

提示:高德开发者Key可通过注册高德开放平台免费获取,个人开发者每日有3000次免费调用额度。

2.2 接口响应数据结构

典型响应示例(山东省济南市):

{ "status": "1", "info": "OK", "districts": [ { "citycode": "0531", "adcode": "370100", "name": "济南市", "level": "city", "districts": [ { "citycode": "0531", "adcode": "370102", "name": "历下区", "level": "district", "districts": [] } // 其他区县... ] } ] }

关键字段说明:

  • citycode:区号(如北京010)
  • adcode:行政区划代码
  • level:行政级别(province/city/district)
  • districts:下级行政区数组

3. Vue 3实现方案

3.1 组件基础结构

使用Vue 3的Composition API和<script setup>语法:

<template> <div class="cascader"> <select v-model="selectedProvince" @change="loadCities"> <option value="">请选择省份</option> <option v-for="province in provinces" :value="province.adcode"> {{ province.name }} </option> </select> <select v-model="selectedCity" @change="loadDistricts" :disabled="!selectedProvince"> <option value="">请选择城市</option> <option v-for="city in cities" :value="city.adcode"> {{ city.name }} </option> </select> <select v-model="selectedDistrict" :disabled="!selectedCity"> <option value="">请选择区县</option> <option v-for="district in districts" :value="district.adcode"> {{ district.name }} </option> </select> </div> </template>

3.2 数据加载逻辑

<script setup> import { ref, onMounted } from 'vue' const provinces = ref([]) const cities = ref([]) const districts = ref([]) const selectedProvince = ref('') const selectedCity = ref('') const selectedDistrict = ref('') // 加载省份数据 const loadProvinces = async () => { const response = await fetch(`${API_URL}?keywords=中国&${new URLSearchParams(baseParams)}`) const data = await response.json() provinces.value = data.districts[0].districts } // 加载城市数据 const loadCities = async () => { const response = await fetch(`${API_URL}?keywords=${selectedProvince.value}&${new URLSearchParams(baseParams)}`) const data = await response.json() cities.value = data.districts[0]?.districts || [] districts.value = [] selectedCity.value = '' selectedDistrict.value = '' } // 加载区县数据 const loadDistricts = async () => { const response = await fetch(`${API_URL}?keywords=${selectedCity.value}&${new URLSearchParams(baseParams)}`) const data = await response.json() districts.value = data.districts[0]?.districts || [] selectedDistrict.value = '' } onMounted(() => { loadProvinces() }) </script>

4. React实现方案

4.1 使用自定义Hook封装逻辑

// useDistrict.js import { useState, useEffect } from 'react' export function useDistrict(apiKey) { const [provinces, setProvinces] = useState([]) const [cities, setCities] = useState([]) const [districts, setDistricts] = useState([]) const [selectedProvince, setSelectedProvince] = useState('') const [selectedCity, setSelectedCity] = useState('') const baseParams = { key: apiKey, extensions: 'all', subdistrict: 1, // 每次只获取下一级 } const fetchDistricts = async (keywords) => { const params = new URLSearchParams({...baseParams, keywords}) const response = await fetch(`${API_URL}?${params}`) return (await response.json()).districts[0]?.districts || [] } useEffect(() => { fetchDistricts('中国').then(setProvinces) }, []) useEffect(() => { if (!selectedProvince) return fetchDistricts(selectedProvince).then(setCities) setDistricts([]) setSelectedCity('') }, [selectedProvince]) useEffect(() => { if (!selectedCity) return fetchDistricts(selectedCity).then(setDistricts) }, [selectedCity]) return { provinces, cities, districts, selectedProvince, selectedCity, setSelectedProvince, setSelectedCity } }

4.2 组件实现

// DistrictSelector.jsx import { useDistrict } from './useDistrict' export function DistrictSelector({ apiKey }) { const { provinces, cities, districts, selectedProvince, selectedCity, setSelectedProvince, setSelectedCity } = useDistrict(apiKey) return ( <div className="cascader"> <select value={selectedProvince} onChange={(e) => setSelectedProvince(e.target.value)} > <option value="">请选择省份</option> {provinces.map(province => ( <option key={province.adcode} value={province.adcode}> {province.name} </option> ))} </select> <select value={selectedCity} onChange={(e) => setSelectedCity(e.target.value)} disabled={!selectedProvince} > <option value="">请选择城市</option> {cities.map(city => ( <option key={city.adcode} value={city.adcode}> {city.name} </option> ))} </select> <select disabled={!selectedCity}> <option value="">请选择区县</option> {districts.map(district => ( <option key={district.adcode} value={district.adcode}> {district.name} </option> ))} </select> </div> ) }

5. 性能优化与高级功能

5.1 数据缓存策略

避免重复请求已加载的数据:

// Vue示例 const cache = new Map() const fetchWithCache = async (key, fetchFn) => { if (cache.has(key)) return cache.get(key) const data = await fetchFn() cache.set(key, data) return data } // 修改loadCities函数 const loadCities = async () => { const key = `city_${selectedProvince.value}` const data = await fetchWithCache(key, async () => { const response = await fetch(`${API_URL}?keywords=${selectedProvince.value}&${new URLSearchParams(baseParams)}`) return (await response.json()).districts[0]?.districts || [] }) cities.value = data }

5.2 防抖与加载状态

// React示例 import { useDebounce } from 'use-debounce' function useDistrict(apiKey) { const [loading, setLoading] = useState(false) const [debouncedProvince] = useDebounce(selectedProvince, 300) useEffect(() => { if (!debouncedProvince) return setLoading(true) fetchDistricts(debouncedProvince) .then(setCities) .finally(() => setLoading(false)) }, [debouncedProvince]) return { // ...其他返回值 loading } } // 在组件中使用 {loading && <span className="loading">加载中...</span>}

5.3 完整地址回填功能

// 根据adcode获取完整路径 const getFullPath = async (adcode) => { const path = [] let current = adcode while (current && current !== '100000') { const response = await fetch(`${API_URL}?keywords=${current}&${new URLSearchParams(baseParams)}`) const data = await response.json() if (!data.districts[0]) break const { name, adcode, parentCode } = data.districts[0] path.unshift({ name, adcode }) current = parentCode } return path } // 使用示例 getFullPath('370102').then(path => { // 输出: [{name: "山东省", adcode: "370000"}, {name: "济南市", adcode: "370100"}, ...] })

6. 错误处理与边界情况

实际项目中需要考虑的各种异常情况:

  1. API限流处理
const fetchDistricts = async (keywords) => { try { const response = await fetch(`${API_URL}?keywords=${keywords}&${new URLSearchParams(baseParams)}`) const data = await response.json() if (data.status !== '1') { throw new Error(data.info || '请求失败') } return data.districts[0]?.districts || [] } catch (error) { console.error('获取行政区划失败:', error) // 显示友好错误提示 return [] } }
  1. 特殊行政区划处理
  • 直辖市(北京/上海/天津/重庆)的层级结构特殊
  • 港澳台地区的adcode规则不同
  • 某些县级市直接隶属于省份(如河南省济源市)
  1. 离线回退方案
// 当API不可用时使用本地缓存 const loadProvinces = async () => { try { const onlineData = await fetchProvincesFromAPI() localStorage.setItem('provinces_cache', JSON.stringify(onlineData)) return onlineData } catch { const cached = localStorage.getItem('provinces_cache') return cached ? JSON.parse(cached) : [] } }

在最近的一个电商项目中,我们采用这种动态加载方案后,表单加载速度提升了40%,而且再也不用担心用户反馈"找不到新设立的区县"问题了。特别是在处理政府类项目时,行政区划数据的准确性直接关系到业务合规性,动态API方案成为了我们的首选。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/28 17:14:10

如何在移动端实现丝滑的PDF预览?pdfh5.js给你答案

如何在移动端实现丝滑的PDF预览&#xff1f;pdfh5.js给你答案 【免费下载链接】pdfh5 项目地址: https://gitcode.com/gh_mirrors/pdf/pdfh5 在移动互联网时代&#xff0c;PDF文档的在线预览已成为众多应用场景的刚需。无论是教育平台的课件展示、企业系统的合同查阅&a…

作者头像 李华
网站建设 2026/4/28 17:11:02

病毒清除验证:模型病毒选错了,申报可能要推倒重来

在生物制品IND或BLA申报环节&#xff0c;监管机构驳回病毒清除验证资料的常见原因之一是——模型病毒选择不当。在病毒清除验证的各个环节中&#xff0c;模型病毒的选择是奠定整个验证工作成败的关键一步。然而&#xff0c;许多的研发团队随意的在对照表中勾选3-4种病毒&#x…

作者头像 李华
网站建设 2026/4/28 17:10:56

缺陷第六感训练:软件测试专家的直觉构建与精进之道

在软件测试领域&#xff0c;我们常常谈论用例设计、自动化脚本、覆盖率分析等可量化、可复制的技术。然而&#xff0c;在众多资深测试专家的工具箱里&#xff0c;还隐藏着一项难以言传却至关重要的能力——“缺陷第六感”。它并非玄学&#xff0c;而是经验、模式识别、系统性思…

作者头像 李华
网站建设 2026/4/28 17:10:26

2025届学术党必备的降重复率平台解析与推荐

Ai论文网站排名&#xff08;开题报告、文献综述、降aigc率、降重综合对比&#xff09; TOP1. 千笔AI TOP2. aipasspaper TOP3. 清北论文 TOP4. 豆包 TOP5. kimi TOP6. deepseek 于内容生产不断推进的进程当中&#xff0c;AI所生成的内容也就是AIGC呈现出常见的模式化以及…

作者头像 李华