ECharts中国地图数据包:china.js注册、下钻与白屏排查

发布时间:2026/10/26 9:43:32
ECharts中国地图数据包:china.js注册、下钻与白屏排查 简介面向使用 ECharts 制作中国地图可视化的前端与数据分析开发者这套 china.js 中国及各地 js 文件压缩包提供可直接引入的地图脚本与省级地理数据用于解决中国及分省区域数据展示中地图配置与数据准备繁琐的问题。压缩包为 rar 格式共 35 个 js 文件大小约 841KB核心文件可配合 ECharts 实例完成地图类型设置、区域样式调整、提示框与缩放漫游等交互配置省级脚本便于按需加载对应区域。已有 5705 人学习下载说明其在图表开发与教学演示场景中具有一定参考价值。读者可获得较完整的地图数据组织方式、现成脚本调用思路和区域可视化实现素材并减少自行查找区域边界数据的时间适合需要快速搭建中国地图看板、分省数据分布图或课程案例的中级前端开发者使用。1. 一个白屏下午引出的地图数据包前几天有人问我运营看板里那块中国地图突然就白了控制台只有一行Map china not exists。他照着老教程写require(echarts/map/js/china)可项目里的 ECharts 已经是 5.x那个目录早就没了。这类问题的解法只有一个来源把 china.js 中国及各省份 JS 地图数据包放回项目里再用registerMap把地图名注册进去。这份资源包本质上是一批几何数据全国一张、各省一份既能当 JS 脚本直接script引入也能当 JSON 走fetch或import加载配合 ECharts 的geo或series-map就能出图。谁适合拿它做数据看板、区域分布统计、门店热力、人口经济指标可视化的前端以及需要离线部署、不能依赖外部地图服务的项目。全国到省份的下钻、散点叠加、区域填色这份包都能撑住。2. 资源包结构拆解china.js、省份 js 与 geoJSON 的对位关系2.1 目录分层与命名规律这种包一般按两层组织map/js/存 JS 版本map/json/存 GeoJSON 版本两个目录里的文件名一一对应。全国的文件名固定是china省级文件按拼音小写命名比如beijing、guangdong、neimenggu、xianggang市级再往下一层用「省拼音 市拼音」的形式。文件体积差别很大全国那份通常在两百 KB 上下省份从几十 KB 到几百 KB 不等西部地广人稀但边界复杂的省份比如新疆、内蒙古反而更大因为多边形节点多。路径内容体积量级典型用途map/js/china.js全国地图UMD 包装引入即注册200KB 左右旧项目、直接 script 引入map/json/china.json全国地图标准 GeoJSON200KB 左右ECharts 5、按需加载map/js/guangdong.js单个省份50150KB省级下钻map/json/guangdong.json同上的 JSON 版同上动态 importmap/js/city/部分包带地级市粒度1080KB市县级精细化选包的时候先看有没有json/目录。只有js/的包在 ECharts 5 下也能用但只能走全局脚本方式拿不到数据本身做数据清洗时很别扭带json/的包可以直接import成对象想加工坐标、过滤区域都方便。2.2 .js 与 .json 的两种载入形式先看 JS 版内部长什么样理解了这段很多报错就能自己定位// map/js/china.js 的典型结构简写 (function (root, factory) { // UMD 包装同时兼容 AMD、CommonJS 和浏览器全局 if (typeof define function define.amd) { define([exports, echarts], factory); } else if (typeof exports object typeof exports.nodeName ! string) { factory(exports, require(echarts)); } else { factory({}, root.echarts); // 浏览器下依赖全局 echarts } }(this, function (exports, echarts) { var log function (msg) { if (typeof console ! undefined) console.error(msg); }; if (!echarts) { log(ECharts is not Loaded); // 顺序错了就报这句 return; } if (!echarts.registerMap) { log(ECharts Map is not loaded); return; } // 核心就这一句把 GeoJSON 挂到地图名 china 上 echarts.registerMap(china, { type: FeatureCollection, features: [ /* 每个 feature 是一个省properties.name 是省名 */ ] }); }));这段代码说明三件事。第一JS 版是「自执行注册」文件加载完地图就已经注册好了不需要你再调registerMap。第二它对加载顺序有硬要求echarts必须先在全局存在否则直接打印ECharts is not Loaded然后静默返回表现就是地图白屏但页面不报错。第三features[].properties.name是后续所有数据对齐的锚点你的数据里name必须和它一致。JSON 版就是去掉外壳只留{ type: FeatureCollection, features: [...] }。用法变成import * as echarts from echarts; import chinaJson from ../map/json/china.json; // 第二个参数必须是 GeoJSON 对象不能是字符串 echarts.registerMap(china, chinaJson);注意registerMap的第二个参数传JSON.stringify后的字符串是不行的会报Invalid geoJson format。有些项目从接口拿到的是字符串记得先JSON.parse。第三个参数是specialAreas用来把某个区域单独挪位置比如把南海诸岛单独摆到右下角用法是echarts.registerMap(china, geoJson, { 南海诸岛: [[126, 25], [130, 25]] })这种坐标范围格式。2.3 一次地图渲染要经过的四个环节从数据到画面中间有四个环节任何一环断了都出不来图。第一个环节是「地图数据就位」也就是registerMap成功执行。第二个环节是「地图名对上」series.map或geo.map写的字符串必须和注册时的名字完全一致大小写、空格都算。第三个环节是「坐标系统一」地图本身是经纬度散点要落在图上就必须声明coordinateSystem: geo并指向同一个地图。第四个环节是「数据落位」series.data里的name要能匹配上features里的properties.name对不上就显示为灰色底图、没有数值。四个环节里最容易翻车的是第一个和第四个。第一个是因为 ECharts 5 之后 npm 包里不再附带地图数据很多教程还在教老路径第四个是因为不同来源的 GeoJSON 省名写法不同老版 ECharts 自带的是「北京」「广东」一些第三方数据源写的是「北京市」「广东省」差一个字就全灰。3. 跑通第一张地图本地服务、registerMap 与 series-map 三步落地3.1 环境准备与引入方式先建目录。地图数据单独放别和业务代码混在一起后面做按需加载好处理mkdir -p map/json src # 把包里的 json 文件拷进 map/jsonjs 文件拷进 map/js # 起静态服务file:// 协议下 fetch 会因跨域被拦 npx serve . -p 5173起服务这一步别省。直接双击 HTML 用file://打开import和fetch都会被浏览器安全策略挡掉报的是CORS policy或者Failed to fetch跟地图本身没关系新手容易在这里绕半天。引入方式有两种看项目类型选方式写法适用注意点全局脚本script srcecharts.min.js后接script srcmap/js/china.js老项目、无构建顺序不能反echarts必须在前模块化import * as echarts from echartsimport chinaJsonVite / Webpack 项目ECharts 5 推荐数据可控如果用的是 ECharts 5用模块化那条路。全局脚本那条只适合维护老页面原因是map/js/china.js在 ECharts 5 的 npm 包里没有只能靠 CDN 或手工拷文件长期维护会乱。3.2 注册与渲染的最小闭环这是一份能直接跑的完整代码把注释看完基本就懂链路了// src/main.js import * as echarts from echarts; import chinaJson from ../map/json/china.json; // 1. 注册地图名字定为 china echarts.registerMap(china, chinaJson); const chart echarts.init(document.getElementById(map)); // 2. 业务数据name 必须和 GeoJSON 里的 properties.name 完全一致 const data [ { name: 北京, value: 820 }, { name: 广东, value: 1200 }, { name: 浙江, value: 960 }, { name: 四川, value: 640 } ]; chart.setOption({ tooltip: { trigger: item, // {b} 是区域名{c} 是 value没有数据的区域 c 为空 formatter: (p) ${p.name}br/指标${p.value ?? 暂无} }, visualMap: { min: 0, max: 1300, left: 24, bottom: 24, text: [高, 低], calculable: true, inRange: { // 从浅蓝到深蓝的连续渐变数值越大颜色越深 color: [#e8f4ff, #1f5fa8] } }, series: [{ type: map, map: china, // 对应 registerMap 的第一个参数 roam: true, // 允许缩放和拖拽 zoom: 1.15, // 初始缩放1 是自适应容器 label: { show: false }, emphasis: { label: { show: true, fontSize: 12 }, itemStyle: { areaColor: #ffd666 } // 悬停高亮色 }, itemStyle: { borderColor: #c9d6e3, borderWidth: 0.6 }, data }] }); // 3. 容器尺寸变化时重绘否则缩放浏览器窗口地图会错位 window.addEventListener(resize, () chart.resize());几个参数值得单独说。map: china是唯一的地图名绑定入口写错就是Map china not exists。roam打开后用户能拖能缩做看板时建议开着但配zoom初始值别太大不然一进来就糊在某个省上。visualMap.min/max要和你的数据量级匹配写死会导致颜色全糊成一片或者全是最浅色稳妥做法是渲染前先算一遍数据的最大最小值const values data.map(d d.value); const option { visualMap: { min: Math.min(...values), max: Math.max(...values) } };emphasis.itemStyle.areaColor是悬停高亮默认颜色在深色背景上几乎看不见一定要手动改。3.3 全国到省份的下钻联动看板类需求里点省份进省、点空白返回全国是出场率最高的交互。核心思路是点中某个省 → 动态加载该省的 GeoJSON → 注册成新地图名 → 重新setOption把map换掉。难点在数据加载和状态记录代码// 省份名到文件名拼音的映射GeoJSON 里是中文名文件名是拼音 const NAME_TO_FILE { 北京: beijing, 广东: guangdong, 浙江: zhejiang, 四川: sichuan, 江苏: jiangsu, 山东: shandong }; // 动态加载省份数据Vite 下用 glob 一次性把 json 都登记进来 const provinceModules import.meta.glob(../map/json/*.json); async function drillDown(provinceName) { const file NAME_TO_FILE[provinceName]; if (!file) return null; // 没有对应文件的地市直接忽略 const loader provinceModules[../map/json/${file}.json]; if (!loader) return null; const mod await loader(); const geoJson mod.default || mod; // 用地名当注册名避免多省份互相覆盖 echarts.registerMap(provinceName, geoJson); return provinceName; } chart.on(click, async (params) { // 只在点中地图区域时响应点散点或空白不处理 if (params.componentType ! series || params.seriesType ! map) return; const mapName await drillDown(params.name); if (!mapName) return; chart.setOption({ series: [{ map: mapName, // 下钻后原本的全国数据对不上省内地市先清空 data: [] }] }); });这里用import.meta.glob是 Vite 的写法作用是提前把目录下所有 JSON 登记成懒加载函数运行时按需import不会打进主包。Webpack 对应的是require.context思路一样// Webpack 版本 const ctx require.context(../map/json, false, /\.json$/); const geoJson ctx(./${file}.json);返回全国的逻辑更简单把map切回china并恢复全国数据即可。真正要小心的是「状态」如果用户连着点了三层全国 → 省 → 市返回时得一层层退所以实际项目里我会维护一个数组当栈每次下钻push返回时pop出上一个地图名。这个栈的写法放到 4.3 节细说。4. 核心配置拆解geo 与 series-map 的取舍、visualMap 配色和数据对齐4.1 geo 与 series-map 不是随便叠的很多人第一反应是「我要在地图上打点那就配一个 geo 再配一个 map」。这两个组件渲染的是同一个几何体同时配会产生两层地图重叠看起来像边框变粗了缩放时两层不同步还会多消耗一倍渲染开销。正确的分工是只需要「区域填色 数值」用series.type map一个组件搞定visualMap直接生效。需要「区域填色 散点 / 飞线 / 涟漪点」用geo做底图散点 series 通过coordinateSystem: geo和geoIndex绑定区域填色则用geo.regions或者再加一个series-map但把geoIndex指向同一个 geo。第二种是容易踩坑的组合写法是这样chart.setOption({ geo: { map: china, roam: true, itemStyle: { areaColor: #f3f7fb, borderColor: #c9d6e3 }, emphasis: { itemStyle: { areaColor: #ffe7a3 } } }, series: [ { type: effectScatter, coordinateSystem: geo, // 关键告诉它用 geo 组件的坐标系 geoIndex: 0, // 指向上面那个 geo symbolSize: 10, rippleEffect: { scale: 3 }, data: [ // 散点数据必须是 [经度, 纬度, 值] 三元组 { name: 某点位, value: [113.27, 23.13, 420] } ] } ] });coordinateSystem: geo不写散点会落在画布左上角堆成一坨。geoIndex在有多个 geo 时必须写否则默认取第一个。value数组的前两位是经度、纬度顺序不能反反了点在国外。还有一个组合上的绝对禁忌series-map的map和geo.map写同一个名字、又没有geoIndex关联时ECharts 会认为是两套独立地图视觉重叠但事件各管各的点击省份可能触发两次。判断方法很简单控制台里chart.on(click)打一下params.componentType如果一次点击打出来两条就是这个原因。4.2 visualMap 的两种模式与数据对齐visualMap分连续型type: continuous默认和分段型type: piecewise。连续型适合展示连续指标比如 GDP、人口分段型适合分档比如「高 / 中 / 低风险区」配色更直观图例也更好读。// 分段型按业务定义的阈值分档比连续型更好讲故事 visualMap: { type: piecewise, pieces: [ { min: 1000, label: 高 (1000), color: #a50f15 }, { min: 500, max: 999, label: 中 (500-999), color: #fb6a4a }, { min: 1, max: 499, label: 低 (1-499), color: #fcbba1 }, { value: 0, label: 无数据, color: #eeeeee } ], left: 20, bottom: 20 }分段的边界要写清楚min和max都是闭区间相邻段之间如果写{ min: 500, max: 999 }和{ min: 1000 }1000 这个值就落进了第二段而不是第一段颜色会对不上业务预期。数据对齐是这一节真正要说的重点。地图上出现大面积灰色九成是名字对不上。排查顺序是这样先看 GeoJSON 里到底写了什么名字// 打印地图里所有区域名拿去和业务数据做 diff const names chinaJson.features.map(f f.properties.name); console.log(names.join(、));然后和业务数据的name做交集对比差集就是灰掉的区域。处理方式有三种改业务数据的name最干净、给地图配nameMap做映射、在注册前直接改 GeoJSON 的properties.name最脏但最快。nameMap的写法是键值对键是「地图里的名字」值是「显示的名字」注意方向别搞反series: [{ type: map, map: china, // 地图里叫「北京」业务数据里叫「北京市」就加这条 nameMap: { 北京: 北京市, 内蒙古: 内蒙古自治区 }, data }]nameMap改的是显示名和匹配名tooltip里显示的也会跟着变如果只是想让数据匹配上但显示保持原样那就老老实实改数据别用nameMap不然图例和提示框会不一致这是血泪经验。4.3 点击事件与下钻状态栈回到 3.3 留的尾巴。单层下钻用一个变量就够了多层下钻必须用栈否则用户乱点几下就回不去了。// 栈里每一项记录地图名、对应的数据、进入时的 zoom 和 center const stateStack [{ map: china, data: nationalData, zoom: 1.15 }]; async function enterMap(mapName, data) { stateStack.push({ map: mapName, data, zoom: 1.1 }); chart.setOption({ series: [{ map: mapName, data, zoom: 1.1 }] }, false); } function backToPrev() { if (stateStack.length 1) return; // 已经在最外层 stateStack.pop(); // 弹掉当前层 const prev stateStack[stateStack.length - 1]; chart.setOption({ series: [{ map: prev.map, data: prev.data, zoom: prev.zoom }] }, false); } // 右键或双击返回上一层比放一个「返回」按钮的交互成本低 chart.getZr().on(dblclick, backToPrev);setOption的第二个参数notMerge传false是关键。传true会把之前所有配置清空重来visualMap、tooltip这些全丢地图会变成纯底图没颜色。只有在你彻底切换数据源、需要重置全部配置时才用true。还有一个细节下钻到省份后series.data里的地市名和全国数据的省名完全对不上如果不清空会有一堆数据匹配不上但也不报错图上是灰的很容易以为是数据问题。所以我在enterMap里强制用新数据覆盖宁可先空着也别留着旧数据骗自己。5. 踩坑排查地图白屏、散点偏移、名称不匹配与包体积5.1 白屏但控制台只有一句 Map not exists现象页面其他部分正常地图容器是空白控制台报Map china not exists或者Map xxx not exists。原因registerMap没执行或者执行时echarts还没就绪。ECharts 5 之后 npm 包不带map目录老教程里的import echarts/map/js/china在 5.x 会直接模块找不到用script引入时如果把china.js放在echarts.min.js前面UMD 包装里if (!echarts) { log(ECharts is not Loaded); return; }会静默退出控制台只有一条 error 日志很容易被别的日志冲掉。解决确认三件事——数据文件真的加载了Network 面板看一眼、registerMap在echarts.init之前或至少在同一次渲染前调用了、series.map的字符串和注册名完全一致。如果用的是打包工具把import chinaJson from ./china.json打在main.js顶部打包器会把它内联根本不会有加载失败的问题。5.2 散点飘到海里或者堆在左上角现象地图正常显示但散点全部集中在一个角落或者整体偏离到海面上。原因两种。一种是没写coordinateSystem: geoECharts 默认用直角坐标系经纬度被当成普通 x/y 画在画布上自然堆在左上角。另一种是坐标系不匹配地图 GeoJSON 是 WGS84 原始经纬度而你的点位数据来自国内某地图服务用的是加密后的坐标两者能差出几百米到上千米视觉上就是点位整体偏移。解决先补coordinateSystem: geo和geoIndex。坐标偏移的话需要确认数据源和地图数据属于同一套坐标系要么把点位转换成 WGS84要么在渲染时做偏移校正。千万别用「手动加个固定偏移量」的土办法不同纬度的偏移量不一样加固定值只会让一部分点准、另一部分更偏。5.3 省份全是灰色数据明明有值现象series.data里数据齐全但地图上所有省份都是同一个浅色tooltip显示数值为空。原因series.data[].name和features[].properties.name不一致。老版 ECharts 自带地图里写的是「北京」「内蒙古」很多第三方 GeoJSON 写的是「北京市」「内蒙古自治区」差一个后缀就匹配不上匹配不上就没有valuevisualMap也就没东西可映射。解决先打印地图里的全部区域名和业务数据做集合差差集就是问题区域。然后选一条路——改数据名推荐最不容易出岔子、加nameMap做映射、或者在registerMap之前直接遍历features把名字改掉。第三种最省事但会污染原始数据二次开发时记得拷贝一份再改别直接改import进来的对象。5.4 首屏加载慢、打包体积翻倍现象只用了全国一张图打包后主包大了几百 KB或者一开始就把三十多个省份全import进来首屏白屏时间明显变长。原因.js版的地图文件是 UMD 脚本打包器没法对它做 tree-shaking全量引进来就是全量体积省份数据如果一个个import写在文件顶部会被全部打进主包哪怕用户从来不下钻。解决全国图用 JSON 版体积一样但可控省份图一律改成动态加载。Vite 用import.meta.glob拿懒加载函数Webpack 用require.context配合import()。判断标准很简单——打开 Network 面板首屏加载完成后如果能看到几十个省份的 json 请求就是写成了静态引入得改。做完按需加载首屏只需要一个全国 JSON剩下的在用户点下去那一刻才发请求。6. 二次开发把地图数据接进自己的数据管道拿到这份包之后地图渲染只是第一步真正值钱的是「数据怎么进来、配置怎么复用」。我在几个看板项目里摸索出一套还算稳的做法可以直接抄。第一层是把地图注册和图表配置拆开。新建一个mapRegistry.js只负责「按名字加载数据 注册地图」返回 Promise// mapRegistry.js —— 单一职责管地图数据的加载与注册 const cache new Map(); // 已注册的地图名避免重复 registerMap export async function ensureMap(mapName) { if (cache.has(mapName)) return mapName; const loader mapLoaders[mapName]; // 由 import.meta.glob 生成的映射表 const mod await loader(); echarts.registerMap(mapName, mod.default || mod); cache.set(mapName, true); return mapName; }这样业务页面里只写await ensureMap(guangdong)不用关心文件在哪、是不是 JSON、注册过没有。cache这一步很重要registerMap同名重复调用在部分版本里会告警加上缓存就干净了。第二层是数据归一化。不管后端返回字段叫province、area还是regionName进图表前统一成{ name, value }结构并且在这里把省名后缀统一掉// 把「北京市」规范成「北京」和地图数据对齐 const normalize (raw) raw.map(item ({ name: item.province.replace(/(省|市|自治区|特别行政区|维吾尔|壮族|回族|)$/g, ), value: Number(item.value) || 0 }));正则里把常见后缀都列上比逐条switch好维护。注意别把「内蒙古」里的「蒙古」误删所以后缀要带上「自治区」整体匹配。第三层是配置模板化。geo、visualMap、tooltip这些跟业务无关的配置抽成一个函数只留数据、颜色区间、地图名三个入参。这样新增一个省份页面就是换个参数的事不用复制三百行 option。验证方法也很实在拿一个省份的真数据跑一遍检查三件事——点击下钻后数据是否清空、双击能否回到全国、visualMap的最大最小值是否跟着数据变化。这三条过了基本就能上生产。我一般还会在开发环境里挂一个console.table打印所有未匹配的name谁的数据脏了一眼就能看出来。说个我自己的教训。早几年做区域看板图省事把三十多个省份的 js 全用script标签写进了 HTML 模板本地跑着没感觉上线之后首屏硬生生多了三秒用户投诉到运营那边才发现。从那以后我每次接地图需求都强制先在 Network 面板看一眼首屏请求数确认省份数据是懒加载才继续往下写业务。地图这块本身不复杂坑全在数据对齐和加载时机上把这两件事做扎实剩下的都是配置。希望帮到你。本文还有配套的精品资源点击获取