简介这份地图数据包面向使用 ECharts 做中国区域可视化的大数据开发者与前端工程师提供全国省级地图及各省下辖城市地图的 JSON 边界数据可直接接入 ECharts 的 geo 与 series 配置用于人口、经济指标等统计数据的区域分布展示。数据以行政区划为单元组织适合中级前端与数据可视化学习者按需取用。压缩包共 863 个文件以 610 个 JSON 地图数据和 250 个 JS 脚本为主另有说明文档与工程配置文件整体约 12.62MB按省份与城市分层组织便于按行政区划检索调用。目前已有 14521 人学习下载。读者可据此快速搭建省—市两级地图底图将区域名称与数值绑定到地图系列结合颜色深浅、分段图例、悬浮提示、缩放平移与高亮交互呈现区域差异JSON 边界坐标和 JS 脚本可直接复用省去地图数据搜集、格式转换与坐标校对环节适合学术研究、商业分析、公众信息展示等需要中国区域数据可视化的场景。1. 从一张全国地图点进某个省这套东西到底解决什么问题在一个区域经营看板上最常见的需求就是这个默认看到全国各省点某个省地图换成这个省的市级行政区划再点一下返回回到全国。做数据可视化的人几乎都接过这种活儿。这个项目把 ECharts 的全国省级地图和每个省对应的市级地图做在了一起一套代码完成两级下钻数据文件按省拆分换一个省的地图就是换一个 JSON。它不是那种把几百个特性堆上去的模板而是把「地图数据怎么组织、下钻状态怎么管、业务名字和边界名字怎么对齐」这三件真正会卡住人的事讲清楚了。正在做区域看板、渠道分布大屏、物流覆盖分析的开发者拿过去改几个颜色和字段就能用刚接触 ECharts 地图、想找一个能直接跑起来的骨架的人也适合从这份代码入门。2. 技术栈与目录省级、市级两套边界数据怎么被同一套代码吃下整套东西没有后端地图数据全是静态文件前端只负责加载和渲染。这种结构的好处是随便丢到一个静态服务器上就能跑原型阶段能省掉一堆接口联调的事代价是所有地图边界都得自己准备好而且命名必须严谨一个字不对就是整块地图空白。先看依赖和目录这是决定后面好不好改的地基。2.1 依赖与环境核心依赖只有 ECharts 一个工程用 Vite 起package.json里能看到的运行时依赖就是 ECharts 一个。这个选择很实际地图系列的边界数据不需要打包工具处理把它当静态资源请求就行而 ECharts 本身整包引入体积确实大但地图 visualMap 散点这套组合拳按需引入的收益不高反而容易漏模块导致type: map不认。{ name: echarts-map-drill, private: true, type: module, scripts: { dev: vite --host, build: vite build, preview: vite preview }, dependencies: { echarts: ^5.5.0 }, devDependencies: { vite: ^5.2.0 } }type: module让所有.js文件按 ESM 解析写 import 不用加.mjs后缀vite --host是为了让同事用局域网 IP 也能打开预览地图这种宽屏的东西在大显示器上看效果才准。Node 建议 18 以上Vite 5 对低版本 Node 不友好这个不值得省。真正有讲究的是 GeoJSON 的位置。它不能放在src/下面用import进来一个省的市级边界动辄几百 KB全国三十多个省全打进 bundle 会让构建时间翻倍而且用户打开页面时根本用不到别的省。常见做法是放进public/maps/运行时拼 URL 去fetch换地图文件连构建都不需要重新跑。echarts-map-drill/ ├── public/ │ └── maps/ │ ├── china.json # 全国feature 是省 │ ├── guangdong.json # 广东feature 是市 │ ├── zhejiang.json # 浙江feature 是市 │ └── ... ├── src/ │ ├── maps/ │ │ ├── index.js # 中文省名 - 注册名 │ │ └── loader.js # 按需 fetch registerMap │ ├── chart/ │ │ ├── render.js # setOption 封装 │ │ └── drill.js # 下钻状态管理 │ └── main.js ├── index.html └── package.json2.2 目录结构数据层、注册层、视图层三层分开上面这棵树看着简单但三层的职责边界是刻意划开的混在一起改起来会非常难受。public/maps/只放数据文件的命名规则是「注册名.json」全国固定叫china.json各省用拼音全拼。这份数据从公开的地图边界数据源下载后基本不用改唯一要注意的是某些数据源的省级文件里会带上「南海诸岛」这类子区域布局会偏删掉或者单独提出来处理。src/maps/负责「名字到文件」的翻译和注册它不知道图表长什么样只管把一段 GeoJSON 变成 ECharts 能认的注册项。这一层是整个项目里最该独立的东西因为它的错误表现是「地图不显示」而不是「报错」调试成本极高。src/chart/往下才是视图render.js管 option 怎么拼drill.js管当前在第几层。这层依赖数据层反过来不成立所以后期想把「点省下钻」改成「下拉框切省」只需要动drill.js和main.js。2.3 注册表中文省名到 GeoJSON 文件名的映射必须照抄这是第一个容易翻车的地方。ECharts 的点击回调给你的params.name是 GeoJSON 里properties.name的原值通常是「广东省」「内蒙古自治区」这种带后缀的全称而文件名一般是拼音。中间必须有一张表这张表的键要和china.json里的名字逐字一致。// src/maps/index.js // 键必须和 china.json 里 feature.properties.name 完全一致 export const PROVINCE_INDEX { 北京市: beijing, 天津市: tianjin, 河北省: hebei, 山西省: shanxi, 内蒙古自治区: neimenggu, 辽宁省: liaoning, 吉林省: jilin, 黑龙江省: heilongjiang, 上海市: shanghai, 江苏省: jiangsu, 浙江省: zhejiang, 安徽省: anhui, 福建省: fujian, 江西省: jiangxi, 山东省: shandong, 河南省: henan, 湖北省: hubei, 湖南省: hunan, 广东省: guangdong, 广西壮族自治区: guangxi, 海南省: hainan, 重庆市: chongqing, 四川省: sichuan, 贵州省: guizhou, 云南省: yunnan, 西藏自治区: xizang, 陕西省: shaanxi, 甘肃省: gansu, 青海省: qinghai, 宁夏回族自治区: ningxia, 新疆维吾尔自治区: xinjiang, 台湾省: taiwan, 香港特别行政区: xianggang, 澳门特别行政区: aomen }; // 反向表返回上一级时用注册名找回中文名 export const NAME_BY_KEY Object.fromEntries( Object.entries(PROVINCE_INDEX).map(([cn, key]) [key, cn]) );这张表里有个血泪经验山西和陕西。两个省的拼音在无调拼音里完全一样很多地图数据源自己也糊涂。工程上必须用shanxi和shaanxi区分后者是陕西的规范拼写多一个 a。如果下载来的文件就叫shanxi.json宁可重命名也别在代码里做特殊判断那种写法三个月后自己都看不懂。NAME_BY_KEY这张反向表看着可有可无但它决定了面包屑和返回按钮的文字显示靠PROVINCE_INDEX现查会导致每次渲染都要遍历一遍对象的键虽然量小但不干净。2.4 加载器按需请求并防止连点重复下载地图注册这一步的核心是echarts.registerMap(name, geoJson)第二个参数的结构必须是{ type: FeatureCollection, features: [...] }。很多数据源下载下来外层还包了一层{ code: 0, data: {...} }直接塞进去 ECharts 不报错只是画不出来这是最坑的一种失败方式。// src/maps/loader.js import * as echarts from echarts; const inflight new Map(); // 进行中的请求防止连点造成重复下载 export function hasMap(registerName) { return Boolean(echarts.getMap(registerName)); } export async function ensureMap(registerName) { if (hasMap(registerName)) return registerName; if (inflight.has(registerName)) return inflight.get(registerName); const task (async () { // BASE_URL 保证部署在子路径下也能取到文件 const url ${import.meta.env.BASE_URL}maps/${registerName}.json; const res await fetch(url); if (!res.ok) { throw new Error([map] ${registerName} 加载失败HTTP ${res.status}); } const raw await res.json(); // 兼容外层包了一层的写法 const geoJson raw.data ?? raw; if (!Array.isArray(geoJson.features) || geoJson.features.length 0) { throw new Error([map] ${registerName}.json 结构不对features 为空); } echarts.registerMap(registerName, geoJson); inflight.delete(registerName); return registerName; })(); inflight.set(registerName, task); return task; }几个参数值得单独说。echarts.getMap(name)返回的是已注册的 GeoJSON没注册返回undefined用它判断能避免重复registerMap重复注册在 5.x 里会覆盖但控制台会一直刷警告噪音很大。inflight这张表解决的是连点问题。用户手快在请求还没回来的时候连点两下同一个省没有这张表就是两次网络请求加上注册顺序不可控很容易出现「先回来的后注册」导致画面闪一下。import.meta.env.BASE_URL是 Vite 注入的部署基路径如果你的项目部署在/dashboard/下面而不是根路径不加这个前缀就是一片 404。这个坑在本地开发时完全看不出来一上测试环境就炸。提示任何「地图加载了但显示空白」的排查第一步都是把echarts.getMap(name)的返回值打印出来看features.length而不是盯着 option 反复改。3. 下钻链路从全国点到某个省一次点击里发生了什么下钻这件事本身的代码量不大难在状态。全国是一层省是第二层用户可能在两层之间来回跳还要处理返回按钮、面包屑、浏览器前进后退。最常见的失败写法是每次点击都重新init一个图表实例跑起来看着能用切几次就开始卡最后内存里堆着七八个 canvas。3.1 初始化一个 DOM 只 init 一次// src/chart/init.js import * as echarts from echarts; export function getChart(dom) { // 同一个 DOM 重复 init 会出现两层 canvas这是残影的主要来源 const exist echarts.getInstanceByDom(dom); if (exist) return exist; return echarts.init(dom, null, { renderer: canvas, devicePixelRatio: window.devicePixelRatio }); }getInstanceByDom是官方给的兜底方法有实例就复用没有才创建。renderer选 canvas 而不是 svg是因为地图的路径点太多svg 在低配机器上 hover 会明显掉帧。devicePixelRatio显式传是为了在 2 倍屏上文字不发虚虽然默认值也是取这个但写在代码里换人接手时不用猜。容器的尺寸必须有明确高度。地图和折线图不一样父容器高度是auto的时候 ECharts 算出来的画布高度可能是 0表现就是「代码全跑通了页面上什么都没有」。3.2 全国地图的 option四块配置各管什么// src/chart/render.js export function buildMapOption(registerName, seriesData []) { return { tooltip: { trigger: item, formatter: (p) ${p.name}br/数值${p.value ?? 暂无} }, visualMap: { min: 0, max: 100, left: 16, bottom: 16, text: [高, 低], inRange: { color: [#e8f3ff, #1f6feb] }, calculable: true }, series: [ { type: map, map: registerName, roam: true, // 允许缩放和拖动 zoom: 1.15, label: { show: true, fontSize: 10 }, itemStyle: { borderColor: #ffffff, borderWidth: 0.6, areaColor: #f2f6fc }, emphasis: { label: { show: true }, itemStyle: { areaColor: #ffd666 } }, data: seriesData // [{ name: 广东省, value: 62 }] } ] }; }map字段就是注册时用的名字写错一个字地图直接空白且不报错。roam: true放开缩放拖动看大屏的人习惯滚轮放大看细节但要注意它会让点击热区跟着变如果同时用了geo组件两边的roam要一致不然会出现「地图放大了但散点没动」。label.fontSize给 10 是因为全国图里省名本身就密字号大了会互相压。itemStyle.borderColor用白色是为了在深色背景上勾出省界这个细节决定了截图好不好看。emphasis.itemStyle.areaColor是 hover 高亮色默认的黄色偏刺眼换成暖橙更耐看。data的每一项必须有name字段且要和 GeoJSON 里的区域名对上value决定 visualMap 的着色。对不上的项不会报错只是那块区域保持默认色这种「部分省份有颜色、部分没有」的现象基本都是名字问题。3.3 点击事件params 里真正有用的只有三个字段// src/main.js import { getChart } from ./chart/init.js; import { buildMapOption } from ./chart/render.js; import { drillTo } from ./chart/drill.js; import { ensureMap } from ./maps/loader.js; const dom document.getElementById(map); const chart getChart(dom); async function boot() { await ensureMap(china); chart.setOption(buildMapOption(china), true); } chart.on(click, (params) { // 只处理地图区域点击visualMap 的点击也会触发这个回调 if (params.componentType ! series) return; drillTo(chart, params.name); }); boot();componentType这个判断非常必要。chart.on(click)是全局事件用户点 visualMap 的滑块也会走进来这时候params.name可能是undefined或者图例文字不拦掉就会触发一次莫名其妙的下钻。params.name是区域全称params.value是这一项的数据值没配 data 就是undefinedparams.seriesIndex在多系列时用来区分点的是地图还是散点。3.4 下钻与回退用一个栈记住我现在在哪一层// src/chart/drill.js import { PROVINCE_INDEX } from ../maps/index.js; import { ensureMap } from ../maps/loader.js; import { buildMapOption } from ./render.js; // 栈底永远是全国栈顶是当前层 const stack [{ key: china, label: 全国 }]; export async function drillTo(chart, clickedName) { const key PROVINCE_INDEX[clickedName]; if (!key) return; // 点到了没有数据的区域直接忽略 await ensureMap(key); stack.push({ key, label: clickedName }); chart.setOption(buildMapOption(key), true); syncBreadcrumb(); } export function drillBack(chart) { if (stack.length 1) return; // 已经在最顶层不做任何事 stack.pop(); const top stack[stack.length - 1]; chart.setOption(buildMapOption(top.key), true); syncBreadcrumb(); } function syncBreadcrumb() { const el document.getElementById(breadcrumb); if (!el) return; el.textContent stack.map((s) s.label).join( / ); }栈里存的是{ key, label }而不是单纯的字符串因为返回时既要重新渲染需要注册名又要更新面包屑需要中文名分开存省得回头再查表。if (!key) return这一行挡掉了「南海诸岛」这类在 GeoJSON 里存在、但没有对应省级文件的区域。不拦的话会请求一个 404 的 JSONPromise 抛错页面上表现为点一下卡住。setOption(option, true)的第二个参数notMerge必须传true。这是下钻功能里最容易漏的一行不传的话 ECharts 会把新旧 series 合并表现是返回全国之后地图上面还叠着上一个省的轮廓或者颜色错乱。这个问题不会报错只能靠眼睛发现属于典型的「后悔药」型 bug。drillBack里没有重新fetch因为ensureMap第一次就注册过了echarts.getMap里还在直接setOption就能出图。这也是把注册和渲染拆开带来的好处返回操作是纯内存操作响应几乎是瞬时的。4. 市级地图渲染业务数据和边界数据对不上的时候怎么办省级地图的 feature 是各个市渲染逻辑和全国图一模一样真正麻烦的是数据。业务库里存的可能是「深圳」「广州」地图边界给的是「深圳市」「广州市」还有数据源写「XX市市辖区」这种。三套名字摆在一起visualMap 不出色是必然的。4.1 名称归一化一套 replace 规则对齐两边// src/maps/normalize.js // 把 广州市 / 广州 / 广州市市辖区 统一成 广州 export function normalize(name ) { return String(name) .replace(/(特别行政区|自治区|自治州|地区|省|市|盟|县)$/g, ) .trim(); } // 按归一化后的名字把业务值灌到地图 features 上 export function alignSeries(geoJson, rows, valueKey value) { const byName new Map(rows.map((r) [normalize(r.name), r[valueKey]])); return geoJson.features.map((f) { const cn f.properties.name; return { name: cn, value: byName.get(normalize(cn)) ?? null }; }); }normalize的正则里把「市」放在最后一位处理是因为「市辖区」这种词如果先命中「市」会剩个「辖区」。「自治州」「地区」「盟」是给地级行政区用的做省级下钻到市级时用得着。alignSeries的入参geoJson就是echarts.getMap(registerName)的返回rows是后端返回的数组形如[{ name: 深圳市, value: 88 }]。返回的value用?? null而不是?? 0因为 0 在 visualMap 里是有颜色的null才会保持默认灰视觉上能一眼看出哪些城市没数据。提示如果某块区域明明有数据却还是灰的把geoJson.features.map(f f.properties.name)打出来和业务数据的 name 做一次差集问题立刻现形。4.2 geo 组件和 map series 的分工散点叠加时不要两个都画地图只画行政区着色的时候一个type: map就够了。但只要想在图上叠气泡、飞线或者图标就会碰到一个经典问题type: scatter配coordinateSystem: geo但 option 里根本没有geo组件散点无处安放直接不显示。正确的写法是在 option 里声明一个geo组件然后让地图 series 通过geoIndex复用它而不是各画各的。// 地图 散点叠加的正确写法 export function buildWithScatter(registerName, seriesData, points) { return { geo: { map: registerName, roam: true, layoutCenter: [50%, 50%], layoutSize: 96%, itemStyle: { areaColor: #f2f6fc, borderColor: #fff }, label: { show: true, fontSize: 9 } }, visualMap: { min: 0, max: 100, inRange: { color: [#e8f3ff, #1f6feb] }, left: 16, bottom: 16 }, series: [ { type: map, geoIndex: 0, // 关键复用上面那个 geo不再自己画一遍 data: seriesData }, { type: effectScatter, coordinateSystem: geo, geoIndex: 0, symbolSize: (val) Math.max(8, val[2] / 6), rippleEffect: { scale: 3, brushType: stroke }, data: points // [[113.26, 23.13, 88], ...] } ] }; }geo组件负责画底图坐标、缩放、标签mapseries 通过geoIndex: 0挂上去只管着色effectScatter也用同一个geoIndex找坐标系。三者的roam由geo统一控制不会出现「地图放大了散点没动」。散点的坐标顺序是[经度, 纬度, 数值]。写成[纬度, 经度]的话点会跑到境外去因为经度范围是 ±180、纬度是 ±90落在国内的点纬度一定是 20~50 之间反过来会超出经度范围看起来就是地图外面飘了一堆点。GeoJSON 标准也是[lng, lat]两边保持一致。4.3 加载策略按需拉 vs 一次性预加载省份多了以后要选一种策略。三十多个省的市级 GeoJSON全部加起来几十 MB让用户打开页面就全下完显然不现实。按需加载是默认选择也是上面ensureMap的做法点到哪个省才拉哪个配合浏览器缓存用户回访同一个省基本是秒开。缺点是第一次点某个省时会有一两百毫秒的空白体验上能感觉到。想补这个空白可以在hover的时候预取而不是等click// 鼠标悬停时预取点击时基本就能直接出图 let hoverTimer null; chart.on(mouseover, (params) { if (params.componentType ! series) return; const key PROVINCE_INDEX[params.name]; if (!key) return; clearTimeout(hoverTimer); hoverTimer setTimeout(() { ensureMap(key).catch(() {}); // 预取失败不影响正常流程 }, 120); });120 毫秒的延迟是为了过滤掉鼠标划过地图时的连续触发不移除的话鼠标横穿全国能同时发起七八个请求。catch(() {})是必要的预取失败时不应该在控制台留一堆红字真正的错误留给drillTo里的ensureMap去抛。只有当省份数量很少比如只做华东六省一市时一次性预加载才划算可以在boot()里Promise.all拉完再渲染。判断标准很简单总文件体积小于 2 MB 就全量大于就走按需。5. 避坑与排查地图空白、名字对不上、状态错乱的五条记录地图这类东西的失败模式很统一——不报错只是没画出来或者画错了。下面这五条是我在实际项目里反复撞上的每条按现象、原因、解决三步记碰到问题时可以按顺序对着排。5.1 渲染不出来先怀疑数据再怀疑配置现象一整块地图空白控制台干干净净一条错都没有。原因通常是 GeoJSON 结构不对。registerMap对入参很宽容你给它一个{ code: 0, data: { features: [...] } }它也照单全收然后什么都画不出来。另一种常见情况是 fetch 的路径 404但 SPA 的 history 模式把所有未匹配路径都回退到index.htmlres.ok是trueres.json()拿到一段 HTML 直接抛解析错误——如果这个错误被catch吃掉了表现同样是空白。解决方式是在ensureMap里加两道断言一道查res.ok一道查features是不是非空数组两道都过不了就抛错。排查时先console.log(echarts.getMap(name)?.features?.length)是undefined就是没注册上是0就是数据有问题两者都有值但屏幕还是空的才去检查 option。现象二地图出来了但位置偏到角落里或者只有一小块。原因是数据源给的 GeoJSON 带了额外区域比如全国图里混进了「南海诸岛」的独立 feature或者某个省的图里带上了邻近海域的轮廓boundingCoords被撑大主体被压缩。解法有两个一是在下载数据时就把多余 feature 剔掉二是渲染时用layoutCenter和layoutSize强制约束。我一般选前者数据干净一次后面所有页面都省事。5.2 数据对不上名字、后缀、编码三处现象三visualMap 图例在部分区域有颜色另一部分永远是灰的。原因几乎一定是名字不匹配。可能是业务数据用了简称「深圳」而地图是「深圳市」也可能是数据源把「XX自治州」写成了「XX州」。还有一类更隐蔽两份数据一个是 UTF-8 一个是 GBK看着一样的汉字在字节层面不同判断失败。解决就是把normalize这套函数提到公共模块业务侧和地图侧都过一遍同一个函数再比对不要只在一边做转换。排查时打差集最快把地图的 feature 名集合减去业务数据的 name 集合剩下的就是没匹配上的。现象四点击某个省完全没反应点别的省都正常。这种「大部分正常、个别失灵」的问题九成是那张映射表写错了。比如把「广西壮族自治区」写成了「广西省」或者「内蒙古自治区」漏了「自治区」三个字。drillTo里if (!key) return会安静地跳过不报任何错所以看起来像点击失效。解决方式是先console.log(params.name)把真实值打出来照着它建表不要凭记忆写。这张表建议直接从china.json里提取生成而不是手敲。5.3 状态错乱setOption 和实例管理现象五下钻之后返回全国地图上还叠着上一个省的轮廓颜色也串了。原因是setOption的第二个参数没传true。ECharts 默认是合并模式新的 series 会按索引往旧的上面合并旧数据残留。地图这种只有单个 series 的场景反而更容易中招因为你根本想不到它会合。解决就是把chart.setOption(option, true)里的true当成硬性规矩地图类图表一律不合并。另外要确保整个页面只init一次实例用getInstanceByDom兜底重复init会在同一个容器里叠两层 canvas缩放时上层不跟随看起来像残影实际是两个图表在打架。窗口尺寸变化后地图不变形靠的是一个ResizeObserver或者window.onresize里调chart.resize()。少这一句的表现是侧边栏一折叠地图还是原来的宽度右边被裁掉一块。6. 二次开发把两级下钻扩成三级顺带接上真实数据两级下钻跑通之后最常见的改造需求是继续往下钻到区县或者反过来在市级图里点某个城市弹出一个详情面板。这两件事看起来要改很多实际只要drill.js里的那个栈设计得当扩展成本很低。往下加一级的关键在于把「层级」这件事从代码里抽出来不再写死china - 省的二元关系。stack里每一项都带上parentKey点击时判断当前栈顶的 key 是不是china不是的话就去加载下一级的地图文件命名规则从guangdong.json变成guangdong-shenzhen.json。// 三级下钻的命名约定level2 用 省-市 拼接 function childKeyOf(parentKey, childName) { if (parentKey china) { return PROVINCE_INDEX[childName]; // 全国 - 省 } // 省 - 市用拼音拼接市名要先转拼音或者用市代码 return ${parentKey}-${CITY_CODE[childName]}; } export async function drillDeeper(chart, clickedName) { const parent stack[stack.length - 1]; const key childKeyOf(parent.key, clickedName); if (!key) return; try { await ensureMap(key); } catch (e) { // 没有更下一级数据时当作普通点击处理比如打开详情面板 openDetailPanel(clickedName); return; } stack.push({ key, label: clickedName, parentKey: parent.key }); chart.setOption(buildMapOption(key), true); syncBreadcrumb(); }childKeyOf里用市代码而不是市名拼拼音是因为「苏州」和「宿州」拼音只差一个字母靠人工维护迟早出错用行政区划代码更稳。ensureMap抛错时不去弹错误提示而是兜底打开详情面板这是一个取舍区县数据不一定每个市都有缺数据时给用户一个有用的反馈比一句「加载失败」体验好得多。接真实业务数据这块改动点其实只有一个函数。后端返回的字段名通常不是name和value中间加一层适配就行// 把接口返回适配成 ECharts 认识的格式 function adaptRows(list, nameField areaName, valField totalAmount) { return list.map((row) ({ name: row[nameField], value: Number(row[valField]) || 0 })); }Number(...) || 0是为了处理接口返回字符串88.5和null的情况Number(88.5)是 88.5 能用Number(null)是 0Number(--)是 NaN 会被|| 0兜住。不要用parseInt它会截断小数金额类的数据差一点就很明显。有一点值得提前想清楚下钻的每一层要不要重新请求数据。全国图的省值和某个省内部各市的值往往来自不同的接口。我的做法是drillTo里并发发起ensureMap和接口请求用Promise.all一起等而不是先等地图再等数据那样点一次要转两次圈。验证改造是否成功我用三个固定动作过一遍连点同一个省十次看网络面板里是不是只有一次请求从全国钻到市再一路返回到全国看面包屑和地图是否同步把窗口从 1920 拖到 800 再拖回去看地图有没有变形或留白。这三个动作覆盖了缓存、状态、自适应三类最容易回归的问题。从那以后我每次接地图类的需求setOption的第二个参数和getInstanceByDom这两处都强制走一遍 checklist宁可多写一行也不省因为这两处的失败都不报错全靠肉眼看。希望帮到你。本文还有配套的精品资源点击获取