技术博客
行政区划查询实战:省市区三级联动选择器前端实现

行政区划查询实战:省市区三级联动选择器前端实现

作者: 万维易源
2026-08-31
行政区划查询省市区联动前端
# 行政区划查询实战:省市区三级联动选择器前端实现 > 接口 / 接入点:行政区划查询(apiCode 1149)· 1149-1 区域查询 + 1149-2 子区域查询 · 免费服务 > 请求方式:POST / GET · 返回格式:JSON · 适用人群:前端 / 全栈工程师 · 阅读时间:约 8 分钟 ## 核心要点 - 三级联动本质是个「选上级 → 用其 id 拉下级 options」的状态机。 - 省用区域查询(1149-1,`level=1`)初始化;市 / 区用子区域查询(1149-2,`parentId`)逐级驱动。 - 免费接口,前端可直接调用(AppKey 放前端仅适合内部 / 演示;生产建议走后端代理,避免泄露 AppKey)。 ## Why:省市区联动是最常见的地址交互 注册、下单、收货地址几乎都要填省 / 市 / 区。手写一份静态字典不仅臃肿还容易过期;用行政区划查询实时拉,数据跟着民政部走,三级之间天然联动。这篇给出一个可跑的前端原型。 ## What:交互与数据流 | 步骤 | 操作 | 调用 | |------|------|------| | 1 | 初始化省列表 | 1149-1,`areaName=中国`,`level=1`(或任意已知省名逐个拉) | | 2 | 选省 → 拉市 | 1149-2,`parentId` = 省的 `id` | | 3 | 选市 → 拉区 | 1149-2,`parentId` = 市的 `id` | > 省列表初始化:可直接用区域查询 `level=1` 逐省查询,或预先缓存全国省级(仅 34 条,适合一次性缓存)。 ## How:前端组件原型(原生 JS) ```html <select id="prov"></select> <select id="city"></select> <select id="dist"></select> <script> const APPKEY = "YOUR_APPKEY"; // 生产环境请改走后端代理,勿在前端暴露 const base = "https://route.showapi.com"; async function loadChildren(sel, parentId) { const url = `${base}/1149-2?appKey=${APPKEY}&parentId=${parentId}&page=1`; const body = await fetch(url).then(r => r.json()).then(d => d.showapi_res_body); sel.innerHTML = body.data.map(o => `<option value="${o.id}">${o.areaName}</option>`).join(""); return body; } async function initProvinces() { const body = await fetch(`${base}/1149-1?appKey=${APPKEY}&areaName=北京市&level=1`).then(r => r.json()).then(d => d.showapi_res_body); const prov = document.getElementById("prov"); prov.innerHTML = body.data.map(o => `<option value="${o.id}">${o.areaName}</option>`).join(""); prov.onchange = () => loadChildren(document.getElementById("city"), prov.value).then(b => { if (b.data[0]) loadChildren(document.getElementById("dist"), b.data[0].id); }); prov.dispatchEvent(new Event("change")); } initProvinces(); </script> ``` ### 后端代理示例(Node.js,避免暴露 AppKey) ```javascript const APPKEY = process.env.SHOWAPI_APPKEY; // 放服务端环境变量 app.get("/api/regions", async (req, res) => { const { parentId } = req.query; const url = `https://route.showapi.com/1149-2?appKey=${APPKEY}&parentId=${parentId}&page=1`; const body = await fetch(url).then(r => r.json()).then(d => d.showapi_res_body); res.json(body.data.map(o => ({ id: o.id, name: o.areaName }))); }); ``` ## 返回示例(市级 options) ```json { "showapi_res_body": { "ret_code": "0", "data": [ {"areaName": "广州市", "id": "440100000000"}, {"areaName": "深圳市", "id": "440300000000"} ], "allNum": "21", "maxSize": "20", "allPage": "2" } } ``` ## 进阶 / 边界 - **AppKey 安全**:免费接口虽无直接资损,但前端暴露 AppKey 仍可被他人冒用 / 触发限流。生产请走后端代理。 - **分页**:下级 > 20 条时分页,联动拉全需循环 `page` 到 `allPage`。 - **缓存**:省级仅 34 条、市级数百条,适合前端 / 边缘缓存(详见缓存设计篇),减少重复请求。 ## FAQ **Q:省列表怎么初始化最快?** 省级数量很少(约 34 条),一次性拉全后缓存即可,不必每次实时请求。 **Q:前端直接放 AppKey 安全吗?** 免费接口虽无资损,但仍有冒用 / 限流风险;生产建议后端代理,AppKey 放服务端。 **Q:联动到区后还要镇 / 村怎么办?** 继续用子区域查询从区的 `id` 下钻;乡镇 / 村委会级也可用区域查询设 `level=4/5`。 **Q:下级超过 20 条联动会断吗?** 会少拉;联动拉全需按 `allPage` 翻页循环,或后端一次性聚合后下发。 ## 相关能力 / 下一步阅读 - [行政区划查询子区域查询实战:用 parentId 逐级下钻省→市→区→街道](https://www.showapi.com/guides/region-query-subregion-1149) - [免费接口也要缓存:行政区划查询低频更新下的缓存设计](https://www.showapi.com/guides/region-query-cache-1149) - [地址录入与校验方案:用行政区划查询规范用户填地址](https://www.showapi.com/guides/region-query-address-1149) - **本系列共 11 篇**:查看[行政区划查询指南总目录](https://www.showapi.com/guides/region-query-guides-1149)