ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Mapbox GL 2.7.0去Token实战:从官方便用限制到自建地图服务

Mapbox GL 2.7.0去Token实战:从官方便用限制到自建地图服务

简介:这是Mapbox GL 2.7.0去Token实践资源包,专为想摆脱固定Token限制的前端开发者和地图应用学习者设计,围绕本地数据源、自建地图服务、匿名公开图层、自定义样式与缓存机制等核心方法,帮助解决在地图初始化、样式加载与瓦片获取过程中无法匿名或离线运行的问题。包体约2000个文件,主体为PBF格式矢量瓦片,另含地图样式JSON、库文件JS/CSS、图标SVG/PNG、字体EOT/WOFF及地图配置文件,压缩后整体仅28.07MB,紧凑而完整。已有2559人学习下载。资源内可直接复用本地瓦片数据及多套样式配置(如城市基础图、卫星底图、自然地球栅格等),基于这些素材可理解自建地图服务、本地GeoJSON、匿名访问公开图层等多种去Token路径,并正确把握与Mapbox的许可边界;尤其适合离线地图、内网部署、二次封装地图组件或在生产环境中规避密钥硬编码风险的开发者参考。资源文件结构清晰,数据、样式与图标分离,便于按需替换和调试;同时这组瓦片与样式也可作为学习Mapbox Style Specification的参考资料,帮助理解样式如何控制地图外观,适合从入门到进阶的阅读与实践。

1. mapbox-gl 2.7.0 去token:把地图从官方密钥里解放出来

“去token”这三个字在 mapbox-gl 2.7.0 这个版本里其实有两层含义:一是去掉写死在代码里的 access token,改为按环境动态注入;二是彻底去掉对官方 token 的运行时依赖,让地图只在自己的瓦片源上工作。很多内网项目就卡在这个地方——地图服务和数据都已经私有化了,但 mapbox-gl 强制在样式请求里带 token,一旦离线或者密钥过期,整张地图直接报废。这篇笔记就讲清楚怎么在 2.7.0 上把 token 从初始化参数、样式资源和请求链路里一项项摘干净,给出一套能在内网、离线环境落地的自建样式方案。适合要私有化部署地图的前端、做可视化大屏但不想被官方配额绑定的开发者。

2. token校验原理与去token的边界:哪些请求必须带密钥

2.1 mapbox-gl 2.7.0 的 token 校验机制

mapbox-gl 在初始化时,只要传入的 style 对象里出现了mapbox://协议的资源地址,无论是最底层的 sources、字体 glyphs 还是图标 sprite,它都会自动把 access token 拼成 URL 参数,向官方域名发起请求。官方样式里常见这样的写法:

{ "sources": { "composite": { "url": "mapbox://mapbox.composite" } }, "glyphs": "mapbox://fonts/mapbox/{fontstack}/{range}.pbf", "sprite": "mapbox://sprites/mapbox/light-v10" }

mapbox-gl 加载时会把上面这些mapbox://全部改写成官方 API 地址,并且自动追加上access_token参数。官方服务器收到请求后校验凭证,如果 token 无效就返回 401,对应请求被浏览器拦下来,地图就渲染不出来。这就是为什么很多人早期只是在初始化里删掉了 token 字段,但样式对象里还是mapbox://,依然会报错——token 根本没“去”干净。真正要去 token,是让所有资源请求都不再指向官方域名。

再往深一层看,2.7.0 对 token 的校验并不是全局强制性的。它只在请求地址能识别出官方资源协议时才追加凭证。换句话说,只要样式里所有mapbox://都被替换成普通的 http/https 地址,mapbox-gl 就完全没有机会接触 token。这个机制就是我们做去 token 方案的地基。

2.2 去 token 的边界:协议替换与资源本地化

做去 token 方案时,需要给样式对象做三处替换,这也是整个改造的核心边界:

  • sources 里的 url 替换为自建瓦片源的 tiles 数组;
  • glyphs 从mapbox://替换为本地字体服务地址;
  • sprite 从mapbox://替换为本地打包的 json 和 png。

只要这三处没有一个再用mapbox://,初始化代码里就可以放心地不传accessToken字段。我给自己定过一条硬规则:凡是项目里出现mapbox://字样的地图样式,一律视作没完成去 token,无论它当时是否报错。因为就算开发时 token 还能用,内网部署那一刻也一定会挂。这条规则帮我挡掉过好几次交付事故。

这里要特别说明一个容易混淆的地方:去 token 不等于去样式。mapbox-gl 支持的样式规范,也就是 version 8 的那套 sources、layers、paint 结构,是完全可以自持的。我们去掉的只是对官方资源的网络依赖,而不是地图的表达能力。图层的填充色、描边、图标、文字标签这些能力全部保留,它们在本地样式对象里就能定义。

2.3 去 token 前后的网络请求对比

为了讲清边界,我把一次正常加载涉及到的请求和去 token 后的请求形态列成一张表。这张表也是后面做验证时候的核对清单:

请求对象去 token 前去 token 后
地图样式本身携带 token 请求官方 style 资源本地 style 对象或本地静态 json
矢量瓦片携带 token 请求官方瓦片接口自建 tiles 服务,无 token
栅格瓦片(影像底图)携带 token 请求官方源自建 xyz 服务,无 token
字体 glyphs携带 token 请求官方字体本地字体 pbf 接口,无 token
sprite 图标携带 token 请求官方雪碧图本地打包的 sprite 资源
地理编码等辅助 API携带 token不接入或自建服务

凡是官方域名下的请求头里出现过access_token的,都得想办法挪走。验证的时候,打开浏览器开发者工具的网络面板,按域名过滤,只要没有官方域名的请求,就可以认为这一步做到了。

3. 去掉官方 token 的最小样式配置:栅格源、矢量源与本地资源

3.1 最小可运行的初始化代码

先写一个最简的、完全不依赖 token 的初始化流程。项目里装好 mapbox-gl 2.7.0 之后,新建一个页面:

<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>去 token 最小示例</title> <style> html, body, #map { width: 100%; height: 100%; margin: 0; } </style> <script src="./mapbox-gl.js"></script> <link href="./mapbox-gl.css" rel="stylesheet"> </head> <body> <div id="map"></div> <script> const map = new mapboxgl.Map({ container: 'map', style: { version: 8, sources: { base: { type: 'raster', tiles: ['https://your-tile-server.com/tiles/{z}/{x}/{y}.png'], tileSize: 256 } }, layers: [ { id: 'base-layer', type: 'raster', source: 'base' } ] }, center: [116.4, 39.9], zoom: 10 }); </script> </body> </html>

这段代码里没有任何accessToken字段。因为 style 里全部使用的是普通的 https 瓦片地址,mapbox-gl 不会往请求上加 token,也不存在向官方域名发送请求的过程。注意 mapbox-gl 的 css 必须引入,否则控件位置、canvas 样式都会乱掉。这个坑我在第一次做内网项目时就踩过,当时只引了 js 没引 css,地图虽然渲染了,但右上角导航控件挤成一团。

参数说明:

  • container 对应页面里的 div 的 id,这个元素必须存在且高度非零;
  • style 里 sources.type 是 raster,tiles 是瓦片地址模板,{z}/{x}/{y}是约定占位符,mapbox-gl 会自动替换;
  • tileSize 对于普通 xyz 栅格瓦片通常设 256,如果你的瓦片来自 ArcGIS 切片且是 512 尺寸,这里要设 512,否则出现重复采样模糊的情况;
  • center 和 zoom 是初始视野,zoom 对应地图缩放级别,数据源支持多高级别就能设多高。

这段代码跑通后,地图页面就已经不再依赖任何官方密钥了。但要注意一点:这段代码里的瓦片服务必须支持跨域访问,否则浏览器会拦图片读取,表现为地图灰块。

3.2 替换官方 sources:选一个能长期自持的栅格源

把官方源去掉之后,最关键的一步是给底图选一个“长期自持”的源。通常有三条路可以走,我分别说下适用场景和坑。

第一条路是直接用自建的成熟瓦片服务。比如用工具发布好的影像图、地形图切片,通常支持 xyz 协议,直接填进 tiles 数组就能用。这类源的优点是稳定可控,缺点是切片要自己维护更新。地图数据变了,切片要重新出,如果不重新出,前端看到的还是老数据。

第二条路是从第三方平台拉取瓦片缓存到内网。这条路我一般不碰,因为版权边界很难讲清楚。做商业项目时,数据授权是一个绕不过去的问题,与其冒着被告的风险,不如直接采购有授权的数据源。

第三条路是用开源工具的栅格源,比如通过标准地图服务协议暴露的接口。这类接口往往返回的是整幅地图图片而不是切片,虽然也能用,但性能和缓存策略都差一些,只适合做原型验证。

选源时主要看三个点:瓦片协议是否支持传{z}/{x}/{y}模板、服务器是否开了 CORS、切片的坐标系是不是 Web Mercator。很多瓦片错位问题都出在坐标系上,而不是代码上。数据源坐标系对不上,改前端的参数怎么都是白费力气。

3.3 矢量源接入:从自建矢量瓦片到本地 glyphs

如果项目需要交互式的高性能渲染,只靠栅格源是不够的。常见做法是接入自建的矢量瓦片源,并同时把字体和图标也本地化。下面是一段完整的矢量源样式配置:

{ "version": 8, "sources": { "vector-layer": { "type": "vector", "tiles": ["https://your-tile-server.com/vector/{z}/{x}/{y}.pbf"], "maxzoom": 14 } }, "glyphs": "https://your-tile-server.com/fonts/{fontstack}/{range}.pbf", "sprite": "https://your-tile-server.com/sprite/sprite", "layers": [ { "id": "building", "type": "fill", "source": "vector-layer", "source-layer": "building", "paint": { "fill-color": "#aaahh" } } ] }

glyphs 字段指向本地字体文件路径,{fontstack}会被替换成字体名,{range}被替换成 Unicode 区间,这个协议格式和官方保持一致,mapbox-gl 才能正确解析。sprite 指向本地资源的同名文件,加载器会自动去读 sprite.json 和 sprite.png。这里最容易被忽略的是 pbf 字体文件本身的跨域配置,如果服务器没返回正确的响应头,字体加载失败后地图上的文字会静默消失,控制台甚至不报错。

替换之后,整个样式文件里不应该再有mapbox://字样。我每次都保存一份检查脚本,对 style 文件做一次全局搜索,如果有命中,就说明去 token 没做完。这一步虽然简单,但很有效。

3.4 本地 style 对象的三种加载方式

  • 方式一:直接把 style 写成 JavaScript 对象,代码直观,适合配置项不多、样式稳定的场景。
  • 方式二:把 style 存成独立 json 文件,用 fetch 在运行时拉取,然后再传给mapboxgl.Map。这样样式和逻辑分离,方便地图样式更新时不用重新发版。
  • 方式三:动态构建 style,把 sources、layers 按环境拼接,适合多租户项目。每个租户的底图、图层、权限都不太一样,构建时各自生成一套样式。

我用得最顺的是方式二加方式三的组合。先把基础样式文件独立出来,按环境注入不同的 sources。真正做私有化交付时,要保证 style 文件的地址也在内网域名下,避免引用外部 CDN 导致离线时被浏览器拦掉。有一个容易翻车的细节:fetch 拉取 style 时,如果服务器返回不了正确的跨域头,地图初始化直接抛错,连瓦片都来不及加载。

4. 生产环境去 token 的完整形态:动态注入与服务端中转

4.1 开发与生产环境解耦:把 token 从代码里剥离

有些项目的实际情况是:地图基础数据已经自建,但某些辅助功能仍然需要官方服务,这时候不能用一刀切的“完全不传 token”方案。更稳妥的做法是,让 token 在构建阶段动态注入,而不是硬编码在源码里。常见做法是利用环境变量:

const config = { map: { token: process.env.MAPBOX_ACCESS_TOKEN, styleBase: process.env.MAPBOX_STYLE_BASE_URL } };

这段代码在构建环境中运行时,process.env会被替换成实际值。源码里不出现真实 token,构建产物里才出现,这样能避免 token 被提交进代码仓库。注意生产环境打包时一定要给环境变量赋值,否则构建后 token 会变成 undefined,地图初始化直接抛错。

如果你是完全内网运行不依赖官方服务的方案,那生产环境的环境变量留空即可,mapboxgl.Map会正常使用自建样式。这比“传一个假 token”更干净,因为传假 token 反而会让部分走官方域名的请求报 401。

4.2 服务端中转:官方资源在业务侧转发

还有一类场景比较棘手:自建了大部分数据,但字体、图标这类资源一时无法本地化,只能指向官方。这种情况我建议加一个服务端中转层:前端请求自己的服务,由服务端附带 token 去拉官方资源再回传。前端代码可以这样处理:

const map = new mapboxgl.Map({ container: 'map', style: { version: 8, sources: { ... }, glyphs: '/api/bridge/fonts/{fontstack}/{range}.pbf', sprite: '/api/bridge/sprite/sprite' } });

这样前端所有请求都打到自己的域名下,token 只存在于服务端配置里,浏览器侧看不到密钥。更重要的是,这样不会产生跨域问题,也方便在服务端加缓存策略,减轻官方接口压力,瓦片和字体这类静态资源缓存住了,重复加载基本不耗时。

需要注意,中转服务要严格限制允许访问的路径,只放行字体和图标这类静态资源,不要做成一个开放的请求出口。否则任何流量都能借你的服务转发到外网,容易被扫到敏感接口,责任说不清楚。

4.3 密钥作用域管控:从源头缩小泄露影响

如果不做彻底去 token,官方 token 仍然无法完全回避,那一定要做作用域管控。这步具体做三件事:

  • 在官方控制台把 token 的域名白名单设置为仅允许生产域名;
  • 给每个环境单独开 token,开发、测试、生产互不共用;
  • token 一旦疑似泄露,立即控制台操作吊销换新,而不是悄悄改前端代码。

这三个点看似是运维工作,但恰恰是去 token 改造里最容易翻车的环节。很多项目到了交付阶段,测试环境 token 能用、生产环境不可用,多半就是域名白名单没加生产域名。把 token 看的比代码还紧一点,是生产环境的基本觉悟。

5. 去 token 常见问题与排查:401、404、字体缺失与 CORS

5.1 初始化报 style 必须存在的错误

现象:启动页面后控制台出现样式缺失异常,地图区域一片空白。 原因:mapbox-gl 2.7.0 要求 style 参数必须是一个合法样式对象,传了空对象或者没有 style 就直接抛错。 解决:确保初始化时传入的对象包含 version、sources、layers 三个字段,且 layers 数组至少有一个有效图层。这个版本对样式结构的校验比早期版本严格,缺少字段会直接中断加载。一个容易踩的坑是,从旧版本升级上来的人习惯了不传 style 让地图自动加载官方样式,这在去 token 场景里完全不适用。

5.2 一切正常但请求里仍有 access_token

现象:网络面板里看到某些请求地址带上 access_token 参数。 原因:style 文件里的 glyphs 或 sprite 还残留mapbox://协议,mapbox-gl 一旦识别到这个协议,就自动补 token 请求官方地址。 解决:把所有mapbox://字符串全局替换为本地地址。如果实在不知道怎么生成字体 pbf,可以暂时把 glyphs 字段置空,并去掉依赖文字标签的 symbol 图层,先把地图跑起来,后续再补字体服务。

5.3 自建瓦片源跨域被拦

现象:瓦片图片加载失败,控制台出现 CORS 报错。 原因:自建瓦片服务器的响应头里没有跨域许可字段,浏览器拦截了图片读取。 解决:给瓦片服务器的响应头加上跨域许可,例如在 nginx 站点配置里加上:

location /tiles/ { add_header Access-Control-Allow-Origin *; expires 7d; }

注意不只是图片,pbf 字体文件同样需要这个响应头。很多团队把图片跨域解决了,但字体文件忘了,导致文字标签出不来,排查大半天才找到原因。

5.4 地图黑屏但没有任何报错

现象:页面不报错,但地图区域完全黑屏,canvas 里什么都没有。 原因:容器 div 的高度是 0。mapbox-gl 用 canvas 渲染,父容器高度塌陷时 canvas 绘制区域为 0。 解决:给容器设置显式高度,比如html, body, #map { height: 100%; },不要依赖内容撑开高度。这个问题在做去 token 改造时极其常见,因为很多人去掉官方样式后,顺手把官方页面模板的基础 css 也删掉了。

5.5 栅格底图重影或瓦片错位

现象:接入自建栅格源后,相邻瓦片之间有明显接缝,或者同一位置出现两次绘制。 原因:tileSize 设错,例如 256 的切片配了 tileSize: 384。也可能是瓦片服务用的坐标系与 Web Mercator 不一致。 解决:先确认瓦片实际尺寸,分别试 256 和 512。再确认瓦片最小缩放、最大缩放与 sources 里的 maxzoom 设置匹配。对坐标系问题,确认源数据发布时选的是 xyz 规范,不要误用 tms,否则 y 轴翻转导致瓦片上下错位。这个从现象上很容易判断:如果错位是上下颠倒的,基本就是 xyz 和 tms 的差异。

6. 验证去 token 是否彻底:两个检查手段与一个微调习惯

6.1 用 Network 面板快速体检

打开浏览器开发者工具,切到网络面板,按域名筛一遍请求,确认列表里没有任何向官方域名发起的请求。这一步的重点不是“看不清就代表没有”,而是要在初始化完成后、地图拖拽缩放后各筛一次。因为某些资源是在交互过程中按需加载的,一进页面没有请求不代表拖动地图时不会发。刚做完去 token 改造时,我就是只看了初始请求,结果拖动地图后突然蹦出两个官方字体请求,当场打脸。

6.2 断网验证法

把浏览器切到离线模式,然后刷新页面。如果地图样式、瓦片、字体、图标都能正常渲染,说明整个运行时链路已经没有对外部资源的依赖了。这个方法比看请求列表更可靠,因为即便有些请求走了公共 CDN,在离线状态也会立刻暴露。做内网交付时,这一步相当于准出测试,项目能不能发版,由它说了算。

6.3 保存一份资源引用清单

我的日常习惯是维护一个样式资源清单,记录当前 style 里用到的每一个 sources、glyphs、sprite 地址,以及它们的域名归属。每次改完样式后跑一遍检查:所有地址是否都在内网域名白名单内,有没有新的mapbox://协议混进来。这份清单看起来费精力,但在项目交接和排障时,能省下大半天查问题的功夫。曾经有一个项目在交付前夜发现部分字体加载失败,就是因为新同事加了一个图层顺便把 glyphs 改回了官方地址,如果没有清单对比,根本不会注意到这行变化。

技巧上,还可以把检查动作塞进构建脚本,用一条命令扫描打包后的 js 和 json 文件,只要命中官方域名或mapbox://关键字就构建失败。我在几个交付项目里都这么做的,最直接的收益是:再也不会有人无意中把一个带 token 的样式文件带进生产包。做去 token 改造不是把 token 从初始化参数里删掉就完事,而是要让整个项目的地图资源不再依赖任何一把官方密钥。这个检查脚本就是最后的守门员,过了它,才算真正落地。这套方法和验证习惯,希望帮到你。

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

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进