☰
ECharts 5地图数据接入:china.js与registerMap实践
2026/10/26 23:59:03 网站建设 项目流程

简介:面向使用 ECharts 绘制中国及省级地图的前端开发者与数据分析人员,这份 JavaScript 地图数据压缩包提供可直接引入的 china.js 及各地地理信息脚本,适合可视化入门、业务看板与区域数据展示,能减少自行整理行政区划边界数据的工作量。rar 压缩包内共 35 个文件,全部为 js 脚本,整体约 841KB,按全国与省级地图数据组织,便于在项目中按需加载并配合 ECharts 的 geo 与 series 配置完成地图渲染、区域着色和交互展示。已有 5705 人学习下载,说明其在同类地图数据资源中具有一定参考价值。读者可获得较完整的中国地图与省级地图数据基础,用于快速搭建地图示例、验证可视化方案,或在此基础上扩展区域数据标注、动态更新与点击交互等功能,省去从零收集和转换地图脚本的时间。

1. ECharts 地图缺的那块数据,就装在这个 china.js 里

接手一个数据看板改造时,最容易被低估的一块是地图。全国分布、省份下钻、区域热度着色,看着是三个需求,落到代码里其实只隔着一个 china.js。ECharts 从 5.0 起把内置地图数据从主包里摘了出去,早年series.map: 'china'直接能跑的日子到此结束,现在不自己备一份地图数据、不手动registerMap,控制台只会回一句地图不存在。这份 echarts china.js 压缩包的价值就在这:一份全国文件加各地省市文件,把 ECharts 地图最缺的那层几何数据补齐。它适合正在做数据大屏、运营看板、区域分布图的前端和数据同学,也适合接手老项目、要把 ECharts 4 的写法迁到 5 的那批人。下面按我实际的接入顺序,从文件结构、加载方式、数据对齐一路讲到几个必踩的坑。

2. 拆开 china.js:UMD 包装、registerMap 与 GeoJSON 的三层结构

很多人第一次打开这个文件,编辑器卡了两秒,滚动条长得看不到底,第一反应是“这玩意儿怎么读”。其实不用全读,它只有三层:最外面一层是模块包装,中间一层是一次registerMap调用,里面一层才是真正有用的地理数据。把这三层拆明白,后面所有报错基本都能自己定位。

2.1 为什么 ECharts 5 之后必须自己带地图数据

ECharts 4 时代,地图数据是跟主库一起发布的,series.map: 'china'写上去就出图,代价是主包体积里长期压着一大坨绝大多数项目用不上的地理坐标。地图数据的更新节奏又跟主库对不上,边界调整、行政层级变化都需要单独发版。把地图数据从主库剥离、改成按需注册,是必然选择,这也是 ECharts 5 之后所有地图项目都要过的第一道关。

剥离之后,渲染链路变成了两段:先用registerMap(名字, 数据)把地图注册进一个内部注册表,再在配置里用series.map或geo.map按名字取用。名字是你自己起的,只要注册和引用对得上就行。这一步不做,图表初始化时会直接抛Map china not exists,页面上一片空白,连坐标轴都不给你留。

所以这类资源包的本质不是“一张图片”,而是一份可以被 ECharts 消费的矢量几何数据集合。压缩包里全国一份、各省各一份,按行政层级切好,你按项目需要引哪份就注册哪份,这也是控制体积的唯一有效手段。

2.2 外层包装:UMD 三段分支到底在干什么

把文件头尾翻出来,典型结构长这样:

(function (root, factory) { // 分支一:AMD 环境 if (typeof define === 'function' && define.amd) { define(['exports', 'echarts'], factory); // 分支二:CommonJS 环境 } else if (typeof exports === 'object' && typeof exports.nodeName !== 'string') { factory(exports, require('echarts')); // 分支三:浏览器全局环境 } else { factory({}, root.echarts); } }(this, function (exports, echarts) { if (!echarts || !echarts.registerMap) { console.error('echarts 未加载,或版本不支持 registerMap'); return; } echarts.registerMap('china', { type: 'FeatureCollection', features: [ // ...成百上千个 Feature ] }); }));

这段包装的逻辑说明:文件本身不导出任何函数,它的作用是在被加载的那一刻执行副作用——也就是调用echarts.registerMap把数据塞进 ECharts 的注册表。三个分支分别对应 AMD 加载器、Node/CommonJS 打包环境、以及浏览器 script 直引。

参数说明上要盯两个点。第一个是registerMap的第一个参数'china',这是地图名,后续series.map: 'china'必须一字不差,改成'中国'你就得在配置里也写'中国'。第二个是第二个参数,也就是那个FeatureCollection对象,它才是真正的地理数据,前面所有包装都是为了把它送进注册表。

需要留个心眼:分支二里的require('echarts')在打包环境里会被静态解析,一旦你的 echarts 版本对不上,或者加载器把它当外部依赖处理失败,就会在这一行炸掉。分支三里的root在严格模式的 ESM 环境下是undefined,此时root.echarts直接抛错。这两个问题在后面的接入章节里会展开。

2.3 里层数据:一个 Feature 里哪几个字段真正参与渲染

再看features数组里的单个元素,结构是标准的 GeoJSON:

{ "type": "Feature", "properties": { "name": "北京市", "adcode": 110000, "center": [116.407526, 39.90403], "cp": [116.407526, 39.90403] }, "geometry": { "type": "Polygon", "coordinates": [ [[116.22, 39.44], [116.24, 39.48], [116.50, 39.52]] ] } }

真正参与渲染的字段只有两个。一个是properties.name,ECharts 用它给每个区域建立索引,后续series.data里每条数据的name就是拿它去匹配的;名字对不上,数据就贴不上去,地图画出来了但全是灰的。另一个是geometry,type决定形状类型,Polygon表示单块区域,MultiPolygon表示由多块互不相连的区域组成,后者在城市、岛屿类数据里很常见,coordinates的嵌套层级也因此多一层。

properties里的其余字段是给业务用的。adcode是行政区划代码,做数据关联时比中文名稳定得多,中文名会变,代码不会。center和cp一般是区域中心点坐标,用来定位标签或者打散点标记,不同来源的文件里这两个字段可能只有一个存在,取用前建议先用console.log确认。

提示:不同来源的 china.js 在properties字段命名上并不统一,有的用name,有的用NAME,前者是 ECharts 能识别的大小写。拿到资源包后先翻一个 Feature 出来看字段名,比出问题再回头查省事得多。

2.4 全国文件和省市文件是平级的,不是嵌套关系

压缩包展开后常见的组织方式是:根目录一份全国文件,省市文件放在一个子目录里,按行政区划分成若干个独立文件。这里有个容易误解的点——省市文件不是从全国文件里“展开”出来的层级子集,它们是各自独立的地图,各自注册各自的名称。

全国文件里每个 Feature 对应一个省级区域,所以properties.name是省一级的全称。省文件里每个 Feature 对应一个地级区域,properties.name是地市一级的名称。做下钻的时候,你做的事其实是:先注册并展示全国地图,用户点了某个省,再注册那份省文件、把series.map换成省地图的名字重新setOption。两份数据从头到尾是平行的,没有包含关系。

这个认知直接决定了数据侧的准备工作。你的业务数据如果只有一份“区域名 + 数值”的扁平表,做全国图时它得匹配省级全称,做下钻图时它得匹配地市级全称,两套名字的粒度不一样。常见做法是准备两张表,或者在一张表里加一列level做区分,别指望一份数据打天下。

另外,全国文件通常体积较大,省市文件相对小。如果项目只需要展示某几个省,全国文件完全可以不引,直接引对应省份文件并把初始map设成该省,首屏体积能省下一大截。这一点在后面讲按需加载时还会提到。

3. 三种接入方式:script 直引、打包器 import 与运行时 fetch

拿到文件之后第一个选择题是“怎么把它弄进项目”。三种方式没有绝对优劣,取决于你的构建工具链和是否需要动态切换地图。我按从土到洋的顺序排,前两种是静态引入,第三种是动态加载,踩坑点各不相同。

3.1 script 标签直引:顺序错一步就是白屏

最原始也最不容易出玄学问题的方式,把 echarts 主库和地图文件都用 script 标签引进来,注意顺序:

<div id="map" style="width: 100%; height: 600px;"></div> <script src="./libs/echarts.min.js"></script> <!-- 必须在 echarts 之后,因为文件内部会读全局 echarts --> <script src="./libs/map/china.js"></script> <script> var chart = echarts.init(document.getElementById('map')); chart.setOption({ tooltip: { trigger: 'item' }, series: [{ type: 'map', map: 'china', // 与 registerMap 里的名字一致 roam: true, label: { show: false }, data: [ { name: '北京市', value: 120 }, { name: '广东省', value: 340 } ] }] }); </script>

逻辑说明:第一个 script 建立全局echarts,第二个 script 在被解析执行时立刻调用registerMap,第三个内联脚本才做初始化。三者是严格的顺序依赖,地图文件的执行结果依赖于echarts已存在。

参数说明:map: 'china'里这个字符串就是要和registerMap的第一个参数完全一致;roam: true开启拖拽和缩放,也可以写成'scale'只允许缩放、'move'只允许平移;data数组里每条的name必须能匹配到properties.name,这个在第四章会细说。

这种写法唯一的硬性要求是顺序。把地图文件放在 echarts 前面,文件执行时root.echarts还是undefined,走到内部判断就直接console.error然后return,注册没发生,后面初始化必然报地图不存在。这种错在本地开发时反而更隐蔽,因为有的浏览器会缓存并重新执行,刷新两次看起来又好了。

还有一个细节:script 直引天然是全局污染,地图文件执行完会在window上什么都不留(它只调注册),但 echarts 本身是全局的。这在纯页面项目里无所谓,在微前端或多实例场景下要留意多个 echarts 实例共存时的注册表共享问题。

3.2 打包器 import:副作用模块与 tree-shaking 的暗坑

上了 webpack 或 Vite 的项目,通常会把这个 js 当作资源文件 import 进来:

import * as echarts from 'echarts'; // 副作用导入:文件自身没有导出,靠执行时调 registerMap 生效 import './assets/map/china.js'; const chart = echarts.init(document.getElementById('map')); chart.setOption({ series: [{ type: 'map', map: 'china' }] });

逻辑说明:这行 import 不接收任何变量,它的作用纯粹是让模块被执行一次。打包器默认会保留有副作用的模块,但前提是它判断得出来这个模块有副作用。

参数说明与配置:webpack 5 和 Vite 都依赖sideEffects字段做摇树优化。如果你把这个地图文件放在src/assets下,而项目的package.json里写了"sideEffects": false,打包器会认为所有模块都没副作用,直接把这行 import 摇掉,结果就是本地 dev 模式正常(dev 不做摇树),生产构建后地图消失。正确做法是显式声明:

{ "sideEffects": [ "*.css", "./src/assets/map/*.js" ] }

这条配置的含义是:除了这两类文件,其他模块都可以安全摇树。地图文件被列进白名单,import 就不会被删。

比摇树更麻烦的是 UMD 包装在 ESM 环境下的分支判定。前面提过,文件里有typeof exports === 'object'这个判断。在 webpack 里,模块被包了一层函数,exports是存在的对象,于是走 CommonJS 分支,执行require('echarts')。这一句在 webpack 里能解析,但如果你的项目装的是 echarts 5,而这份地图文件是按 4 的接口写的,注册时可能静默失败。在 Vite 的 ESM 环境下,require根本不存在,同时顶层this是undefined,root.echarts直接抛Cannot read properties of undefined。

注意:如果你的构建工具是 Vite,直接 import 这类 UMD 包装的 china.js 大概率报错,别在包装层上死磕。要么让后端或静态目录提供纯 GeoJSON 版本,走下面的运行时方案;要么把文件改写成 ESM 形式,把registerMap调用放进一个显式导出的函数里,由业务代码主动调用。

改写成 ESM 其实很简单,把外层包装剥掉,改成导出一个注册函数:

// map/china.mjs import * as echarts from 'echarts'; import geoJson from './china.geo.json'; export default function registerChina() { echarts.registerMap('china', geoJson); }

这样摇树、分支判定、全局变量的问题一次性全解决,代价是你得先把 GeoJSON 从 js 文件里提取出来,这一步可以用第 6 章的脚本做。

3.3 运行时 fetch:按需加载与浏览器缓存

项目里地图数量多、或者需要根据用户权限动态决定加载哪些省份时,静态 import 就不合适了,全部打进包里体积扛不住。这时改成运行时拉取纯 GeoJSON:

async function loadMap(name, url) { const res = await fetch(url, { cache: 'force-cache' }); if (!res.ok) throw new Error(`地图加载失败: ${url} ${res.status}`); const geoJson = await res.json(); // 同名注册会覆盖旧地图,下钻时正好用得上 echarts.registerMap(name, geoJson); return geoJson; } loadMap('china', '/static/map/china.geo.json').then(() => { chart.setOption({ series: [{ type: 'map', map: 'china' }] }); });

逻辑说明:先请求、再解析、再注册,最后才 setOption,三步是串行的,任何一步提前都会导致地图引用不到。cache: 'force-cache'让浏览器优先用本地缓存,避免每次进页面都重新下载几百 KB。

参数说明:name是注册名,允许和已有的重名,重名会覆盖,这个特性在下钻场景里是刚需;url建议放在静态资源目录下,别塞进打包产物,方便单独更新地图数据而不动业务代码。

这个方案的代价是首屏多一次网络往返,所以常见的折中做法是全国图静态引入保证首屏,省市图运行时按需拉。这种混合策略在大屏项目里很实用:用户九成时间只看全国图,下钻是少数操作,没必要为小概率操作拖慢首屏。

3.4 容器初始化的几个必设参数

不管走哪种加载方式,初始化那一步的参数配错,后面地图画得再对也是白搭:

const el = document.getElementById('map'); const chart = echarts.init(el, null, { renderer: 'canvas', // 'canvas' | 'svg' devicePixelRatio: window.devicePixelRatio, locale: 'ZH' }); // 容器尺寸变化时手动重算,ECharts 不会自动监听 const ro = new ResizeObserver(() => chart.resize()); ro.observe(el);

逻辑说明:ECharts 初始化时只读一次容器尺寸,容器是百分比宽度时,父级变化不会触发图表重绘,必须显式调resize()。用ResizeObserver比监听 window 的 resize 更准,因为它能捕获侧边栏折叠、标签页切换这类不改变窗口尺寸的布局变化。

参数说明:renderer选canvas还是svg有讲究,区域数量多、交互频繁的全国地图用 canvas 更稳,区域数量少、需要导出矢量图的用 svg。devicePixelRatio在高分屏上不传会用默认值,导致地图边缘发虚,尤其标签文字明显。

还有一点容易被忽略:init时容器必须有确定的宽高。如果容器此时是display: none或者高度为 0,图表初始化出来尺寸就是 0,后面即使显示出来也是空白,必须补一次resize()。

4. 把数据贴到地图上:name 对齐、geo 与 series 分工、省市下钻联动

地图画出来只是第一步,真正决定这个项目能不能用的是数据能不能正确贴上去、交互能不能顺畅下钻。这一章是整篇里最容易翻车的部分,因为错误表现往往不是报错,而是“图出来了,但是灰的”,没有控制台提示,纯靠经验排查。

4.1 geo 组件和 series-map 到底该用哪个

ECharts 里画地图有两条路,新手最容易混着用,结果画出两层地图叠在一起。

对比项geo组件series.type: 'map'
定位纯地理坐标系底图带数据映射的地图系列
数据绑定不支持直接绑data支持data,可接visualMap
多图层可叠加散点、线、飞线同一地图多系列会重复绘制
典型用途打点、轨迹、热力图底图区域着色、下钻

选型原则很直白:只需要底图、上面要叠散点或飞线的,用geo;需要按数值给区域上色的,用series-map。两者同时配置时,ECharts 会画两遍地图,边界线颜色叠加后会变深,看起来像描边加粗了,实际是重复绘制。

真要用“底图 + 数据层”的组合,正确姿势是把geo作为坐标系,让series通过geoIndex挂上去,而不是各自画一遍:

option = { geo: { map: 'china', roam: true, itemStyle: { areaColor: '#0f2a4a', borderColor: '#2c5f8a' } }, series: [{ type: 'scatter', coordinateSystem: 'geo', // 挂到 geo 上,而不是自己再画一张 geoIndex: 0, data: [{ name: '某市', value: [116.4, 39.9, 88] }] }] };

参数说明:coordinateSystem: 'geo'是关键,它告诉 series 用第 0 个 geo 组件做坐标换算;geoIndex在有多个 geo 时才需要显式指定。散点数据的value是[经度, 纬度, 数值]三段式,前两位决定了点的位置,第三位是给 tooltip 和 visualMap 用的。

4.2 name 对齐:全名、简称和后缀,三选一必须统一

这是地图项目里最高频的问题,没有之一。数据贴不上去,九成是名字对不上。

ECharts 匹配区域的依据是字符串全等。properties.name写的是“北京市”,你数据里写“北京”,匹配失败;反过来也一样。中间差一个字、多一个“省”字、少一个“市”字,都不行。

先确认地图文件里的名字长什么样:

// 在注册后把地图里的名字打出来,建立一份对照表 const geoJson = await fetch('/static/map/china.geo.json').then(r => r.json()); console.table(geoJson.features.map(f => ({ name: f.properties.name, adcode: f.properties.adcode })));

逻辑说明:这段把地图里所有区域的name和adcode打成表格,方便和业务数据做比对。实操里我一般把它跑一次,把结果复制到 Excel 里和业务表做一次 VLOOKUP,比在代码里反复试快得多。

如果业务侧的中文名实在改不了,用nameMap做一层映射:

series: [{ type: 'map', map: 'china', nameMap: { '北京市': '北京', '广西壮族自治区': '广西', '新疆维吾尔自治区': '新疆' }, data: [{ name: '北京', value: 120 }] }]

参数说明:nameMap的键是地图里的原始名(properties.name),值是你要显示和匹配的名字。配置之后,series.data里的name要写映射后的值,tooltip里显示的也是映射后的值。这一点搞反了,数据照样贴不上。

比中文名更稳的做法是关联adcode。业务数据里存一份 adcode,用 adcode 做匹配键,中文名只用来显示,行政区划改名也不会影响你的数据关联。代价是要多维护一份映射,但从长期维护角度看是值的。

4.3 省市下钻:注册覆盖、状态还原与返回全国

下钻的完整链路是:点全国图的某个省 → 加载该省的 js 或 geojson → 注册 → 把series.map换成该省 → 重新setOption→ 用户点返回 → 换回全国。

let currentMap = 'china'; // 默认点亮后重新设置地图名和数据 function renderMap(mapName, data) { currentMap = mapName; chart.setOption({ series: [{ type: 'map', map: mapName, roam: true, data }] }, { notMerge: true }); // 必须 notMerge,否则旧系列的配置会残留 } // 点击下钻 chart.on('click', async (params) => { if (currentMap !== 'china') return; // 防止在省图里再触发 const target = await loadMap(params.name, `/static/map/province/${params.name}.geo.json`); renderMap(params.name, buildData(params.name)); }); // 返回全国 function backToChina() { renderMap('china', nationalData); }

逻辑说明:setOption的第二个参数{ notMerge: true }是这里的关键。默认是合并模式,新旧 series 配置会合在一起,切换地图后可能出现上一张图的样式残留、或者 series 数组变成两个。加上notMerge表示完全替换,每次都是干净的。

参数说明:params.name是用户点击的区域名,可以直接拿来做文件名,但要注意中文字符做 URL 路径需要encodeURIComponent,且部分服务器对中文路径支持不好,稳妥做法是维护一份“名称 → 拼音或 adcode”的映射表,用后者拼路径。

还有一个常见需求是“下钻后要能一键回全国”。实现上就是把renderMap('china', nationalData)再调一次,但要注意registerMap不会因为切换而卸载,全国地图一直注册在表里,直接切换即可,不需要重新加载数据。这一点比很多人想的简单。

4.4 visualMap、roam、label 的参数取舍

最后这组参数是决定观感的:

参数常用值说明
visualMap.min/max按数据算写死会导致颜色分布失真,建议从数据里取极值
visualMap.inRange.color灰度到主色渐变颜色越多,读图成本越高,三到五档够用
visualMap.calculabletrue开启拖拽手柄,大屏场景一般关掉防止误操作
roamtrue/'scale'大屏展示建议只开'scale',防止用户把地图拖出视野
label.show区域少开,区域多关全国图开 label 在小屏上必然糊
layoutCenter/layoutSize['50%','50%']/'90%'控制地图在容器里的位置和占比,比zoom更可控
option = { visualMap: { min: 0, max: 500, calculable: false, inRange: { color: ['#e8f4ff', '#9ec9f0', '#3f7fbf', '#1b4b7a'] }, left: 20, bottom: 20 }, series: [{ type: 'map', map: 'china', roam: 'scale', layoutCenter: ['50%', '50%'], layoutSize: '92%', label: { show: false }, emphasis: { label: { show: true, fontWeight: 'bold' } } }] };

参数说明:layoutSize用百分比表示地图占容器的比例,比调zoom更直观,因为zoom是相对默认缩放倍数的乘法,不同容器尺寸下表现不一致。emphasis里单独开 label 是常用折中——平时不显示名字,鼠标悬停时显示,既清爽又不丢信息。roam: 'scale'限制只缩不拖,避免用户在大屏触屏上把地图拖走又不知道怎么回来。

5. 排查清单:五类翻车现场和对应的定位手法

下面这几条是我在不同项目里反复遇到的,现象看着各不相同,根因其实都落在前面几章的某个环节上。按“现象 → 原因 → 解决”整理,出问题时对照着查。

5.1 报Map china not exists

现象:控制台明确报地图不存在,图表容器空白。原因通常有三个:地图文件没引进来;引进来了但在 echarts 之前执行;或者文件执行时 echarts 版本太老不支持registerMap。解决方式是先在控制台敲echarts.getMap('china'),返回undefined说明注册压根没发生,去看网络面板里那个 js 有没有 200;返回对象说明注册成功了,问题在引用名,检查series.map的字符串和注册名是否一字不差,包括有没有多余空格和大小写。

5.2 地图出来了,但区域全是灰色

现象:边界线正常、形状正常,就是数据不上色,tooltip 也不显示数值。原因基本锁定在 name 不匹配。解决方式是选中一个本应上色的区域,看properties.name到底是什么,再和series.data里的name逐字符比对。特别注意三种情况:简称与全称混用;数据侧带空格;以及用了nameMap之后数据侧还在写原始名。最省事的验证办法是临时把series.data改成一条{ name: '地图里某个确定存在的名字', value: 100 },能上色就说明逻辑没问题,纯粹是数据侧名字的问题。

5.3 本地开发正常,打包上线后地图消失

现象:dev 模式一切正常,CI 构建出来的包一打开就是空白,控制台也不报错。原因有两个:一是 tree-shaking 把副作用 import 删了,二是 UMD 分支在构建产物里判到了错误的分支。解决方式是先在package.json的sideEffects白名单里加上地图文件路径,重新构建验证;如果还不行,把构建产物里搜一下有没有registerMap字样,搜不到就是被删了,搜得到就是执行顺序或者分支判定问题,改用第 3 章里导出一个注册函数的 ESM 写法,让调用时机变成显式的。

5.4 两张地图叠在一起,边界线发黑

现象:地图整体比预期暗,边界线像是加粗了,鼠标移上去还会闪。原因是同时配了geo和series.type: 'map',两个图层各画了一遍。解决方式是二选一:要用数据上色就删掉geo,只留series-map;要用散点底图就删掉map系列,让散点通过coordinateSystem: 'geo'挂到geo上。改完刷新如果还有重影,检查一下代码里是不是有多个setOption调用没加notMerge,配置被合并累积了。

5.5 缩放卡顿、标签糊成一团

现象:全国图开roam之后拖动明显掉帧,或者label一开满屏文字重叠看不清。原因是全国地图的路径节点数量大,canvas 在每次缩放时都要重绘全部路径,同时标签排版也是一次完整计算。解决方式是:只开roam: 'scale'减少重绘触发频率;label改成emphasis下才显示;如果数据精度过高(比如沿海岸线的点特别密),用第 6 章的简化手段把坐标精度降到小数点后三位,视觉上几乎看不出差别,渲染开销能降不少。区域数量本来就少的省份图可以反过来,把 label 开着,不影响性能。

6. 上线前的一道工序:用 Node 脚本自检地图数据并瘦身

地图这类资源有个特点:出问题不报错,只表现为“图不对”,等上线后被业务方截图指出来就晚了。所以我现在养成一个习惯,地图文件进项目之前先跑一遍自检,把名字列表、Feature 数量、重复项都打出来,和业务数据做个交叉比对,比在浏览器里反复刷新快得多。

这个脚本用 Node 的vm模块把 china.js 在一个沙箱里跑一遍,给沙箱注入一个假的echarts.registerMap,把数据截获下来:

// tools/check-map.js const fs = require('fs'); const vm = require('vm'); const file = process.argv[2]; const code = fs.readFileSync(file, 'utf8'); let captured = null; // 沙箱里放一个最小的 echarts 打桩对象 const sandbox = { echarts: { registerMap(name, data) { captured = { name, data }; } }, console }; sandbox.window = sandbox; sandbox.self = sandbox; vm.createContext(sandbox); vm.runInContext(code, sandbox, { timeout: 5000 }); if (!captured) { console.error('未捕获到 registerMap 调用,检查文件格式'); process.exit(1); } const { name, data } = captured; const features = data.features || []; const names = features.map(f => f.properties.name); const dup = names.filter((n, i) => names.indexOf(n) !== i); const weird = names.filter(n => !n || n.trim() !== n); console.log('注册名:', name); console.log('区域数量:', features.length); console.log('重复名称:', dup.length ? dup : '无'); console.log('空名或首尾空格:', weird.length ? weird : '无'); console.log('前 10 个名称:', names.slice(0, 10).join(' / '));

逻辑说明:沙箱里只注入echarts和console,地图文件的 UMD 包装会判断typeof exports,沙箱里没有exports,于是走全局分支,读root.echarts,正好拿到我们塞进去的打桩对象。runInContext加timeout是防止文件里有意外逻辑卡死。

参数说明:命令行第一个参数是文件路径;输出的“注册名”要和业务代码里的series.map对上;“区域数量”可以和预期值比对,少一个就说明数据版本不对;“重复名称”非空是很危险的信号,说明有同名区域,数据会被后一个覆盖。

瘦身用的是 mapshaper,不需要装什么重型依赖,npx直接跑:

npx mapshaper china.geo.json \ -simplify 15% keep-shapes \ -filter-fields name,adcode \ -o format=geojson precision=0.001 china.slim.json

逻辑说明:-simplify 15%是顶点保留比例,15% 意味着丢掉 85% 的路径节点,全国图在这个比例下视觉上基本无损,沿海岸线也不会出现明显锯齿;keep-shapes防止面积很小的区域被简化到消失;-filter-fields只保留两个必要字段,砍掉大量业务用不上的属性;precision=0.001把坐标小数位压到三位,这个精度下 1 公里左右的偏差,做省级着色完全够用。

参数说明:简化比例不是越小越好,低于 5% 会出现边界明显变形、飞地丢失。我一般先跑 15%,把产物和原图叠在一起看一眼,没问题再往下压。另外-filter-fields里一定要带上name,这是 ECharts 匹配区域的唯一依据,丢了它整份数据就废了。

从那以后我每次往项目里加一份新地图文件,都会先跑一遍自检脚本、再跑一遍简化、再和业务数据的名字做一次比对,三步走完才提交。这样拦下来的问题至少有四五次,比上线后被截图反馈再回头查要省心。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询