经纬志
首页 / deck.gl / 教程

瓦片图层:TileLayer 与 MVTLayer

先看结论

数据超过几十万条时,一次性下载和建模都不可行。瓦片图层把请求拆到不同缩放级别, 只加载当前视野需要的切片,渲染再交给子图层完成。

最后验证 2026-11-03 · deck.gl 9.0 · 难度:进阶 · 核验 2026-10-29 · 引用本文

TileLayer 负责调度,不负责画

TileLayer 本身不渲染任何内容。它按 Web 墨卡托切分视野, 为每个瓦片请求数据,然后调用 renderSubLayers, 由你返回一个真正的绘制图层。props.tile 里带着瓦片边界与索引。

var parcelsTile = new deck.TileLayer({
  id: 'parcels-tiles',
  data: 'https://tiles.example.com/parcels/{z}/{x}/{y}.geojson',
  minZoom: 8,
  maxZoom: 16,
  tileSize: 256,
  maxRequests: 8,
  refinementStrategy: 'best-available',
  renderSubLayers: function (props) {
    var west = props.tile.bbox.west;
    var south = props.tile.bbox.south;
    var east = props.tile.bbox.east;
    var north = props.tile.bbox.north;

    return new deck.GeoJsonLayer({
      id: props.id,
      data: props.data,
      filled: true,
      stroked: true,
      getFillColor: [31, 95, 208, 90],
      getLineColor: [255, 255, 255, 200],
      getLineWidth: 1,
      lineWidthUnits: 'pixels',
      pickable: true
    });
  }
});

子图层的 id 必须带上瓦片编号,直接复用 props.id 最省事。子图层数据已经由 TileLayer 解析好,GeoJsonLayer 直接接收对象即可。

瓦片边界不需要手动裁剪。服务端按瓦片切好的数据只包含该瓦片内的几何, bbox 主要用于定位标注或计算密度,不是用来裁剪的。

MVTLayer:少一层封装

MVTLayer 继承自 TileLayer,内置了 MVT 解析和子图层生成。 一个 MVT 图层内部会自动按几何类型拆成面、线、点三类子图层, 样式通过同名访问器传给它们。

var mvt = new deck.MVTLayer({
  id: 'base-mvt',
  data: 'https://tiles.example.com/base/{z}/{x}/{y}.pbf',
  minZoom: 0,
  maxZoom: 14,
  binary: true,
  getFillColor: function (f) {
    var kind = f.properties.kind;
    if (kind === 'water') return [20, 80, 160, 200];
    if (kind === 'park') return [60, 160, 90, 180];
    return [230, 230, 230, 120];
  },
  getLineColor: [255, 255, 255, 180],
  getLineWidth: 1,
  lineWidthMinPixels: 1,
  pickable: true
});

binary: true 让 MVT 以二进制属性表加载,内存占用和解析时间都更低, 代价是访问属性要通过 f.properties 的代理对象,不能直接改原始 JSON。

方案数据格式样式控制适用
直接 GeoJsonLayer整份 GeoJSON完整几千个要素,一次加载
TileLayer + GeoJsonLayer按瓦片切的 GeoJSON完整十万级,服务端已切片
TileLayer + 任意子图层自定义 JSON完整聚合结果、格网等非矢量数据
MVTLayerMVT按几何类型统一底图、大规模行政区划

请求调度与失败处理

TileLayer 按视口计算需要哪些瓦片,并限制同时进行的请求数。 内网服务可以适当调高并发,公网服务则要保守,避免浏览器队列拖慢交互。

var tuned = new deck.TileLayer({
  id: 'parcels-tuned',
  data: 'https://tiles.example.com/parcels/{z}/{x}/{y}.geojson',
  tileSize: 256,
  minZoom: 8,
  maxZoom: 16,
  maxRequests: 12,
  renderSubLayers: function (props) {
    return new deck.GeoJsonLayer({
      id: props.id,
      data: props.data,
      filled: true,
      getFillColor: [31, 95, 208, 90]
    });
  },
  onTileError: function (tile, error) {
    console.warn('瓦片加载失败', tile.id, error.message);
  }
});

单个瓦片失败不会中断整个图层,画面上只会留下一块空白。 onTileError 是把这类问题暴露出来的最直接方式。

注意事项

  • 瓦片地址模板里的 {z}/{x}/{y} 由 TileLayer 自动替换,服务端若是 TMS 方案需要自行翻转 y。
  • tileSize 要与切片服务一致,256 的服务按 512 请求会拿到错位内容。
  • refinementStrategy 决定低级别瓦片何时被替换,best-available 视觉最稳,never 最省流量。
  • 瓦片缓存默认按视口保留,快速平移再返回时会重新请求,可调大 maxCacheSize。
  • 子图层数量随瓦片数增长,每帧都会遍历,图层多时把不必要的高级别瓦片限制掉。
  • MVTLayer 读取的源图层名由切片决定,属性名消失通常意味着服务端换了切片方案。

先看请求数再看帧率。瓦片类问题大多出在请求过多或层级过深。 用浏览器网络面板确认同一时刻的并发请求数,比盯着 fps 更容易定位。

引用本文

经纬志(2026)。《瓦片图层:TileLayer 与 MVTLayer》。https://www.jingweizhi.com/deckgl/tutorial/tile-layers.html(访问日期 2026-11-03)。