经纬志
首页 / Turf.js / 教程

性能与大数据

先看结论

Turf 是纯 JavaScript 的同步计算库,跑在主线程上。 数据量到十万级时,瓶颈不是算法复杂度,而是每次调用产生的对象分配与遍历。 优化方向很明确:减少调用次数、缩小数据范围、把计算移出主线程。

最后验证 2026-11-03 · Turf.js 7.x · 更新 2026-10-29 · 引用本文

先看规模

规模可以怎么做注意
万次以内主线程直接算注意不要在 mousemove 里调用
一万到十万包围盒预筛、批量 coordEach避免逐要素构造 Feature
十万到百万Web Worker + 分块主线程只负责渲染,不参与几何计算
百万以上后端空间数据库、预计算瓦片前端只取聚合后的结果

先用外包矩形缩小候选集

点面判断的成本主要在多边形顶点数。先用外包矩形筛掉明显不相交的点, 再用精确判断,通常能去掉 90% 以上的调用。

var zone = turf.polygon([/* 上百个顶点 */]);
var box  = turf.bbox(zone);        // [西, 南, 东, 北]

function inBox(lng, lat) {
  return lng >= box[0] && lng <= box[2] &&
         lat >= box[1] && lat <= box[3];
}

var candidates = [];
turf.coordEach(pointsFC, function (coord) {
  if (inBox(coord[0], coord[1])) candidates.push(coord);
});

var hits = [];
for (var i = 0; i < candidates.length; i++) {
  if (turf.booleanPointInPolygon(candidates[i], zone)) {
    hits.push(candidates[i]);
  }
}

console.log('候选', candidates.length, '命中', hits.length);

booleanPointInPolygon 接受坐标数组而不只是 Point 要素, 省掉包装对象。面的引用在整个循环里保持一致,避免重复解析。

把计算放进 Web Worker

Worker 里无法直接使用主线程的变量,但可以引入同一份 Turf。 主线程只发送坐标与参数,Worker 回传结果,渲染与计算彻底分开。

// main.js
var worker = new Worker('geo-worker.js');

worker.onmessage = function (e) {
  if (e.data.type === 'buffer') {
    map.getSource('zone').setData(e.data.result);
  }
};

worker.postMessage({
  type: 'buffer',
  center: [116.391263, 39.907359],
  radiusKm: 1.2
});

// geo-worker.js
importScripts('https://cdn.jsdelivr.net/npm/@turf/turf@7/turf.min.js');

self.onmessage = function (e) {
  var msg = e.data;
  if (msg.type !== 'buffer') return;

  var pt = turf.point(msg.center);
  var zone = turf.buffer(pt, msg.radiusKm, { units: 'kilometers' });

  self.postMessage({ type: 'buffer', result: zone });
};

传大数据时要留意结构化克隆的成本:十万个点的 FeatureCollection 在两个线程之间复制一次可能需要上百毫秒。能用 TypedArray 传坐标数组, 就别传整份 GeoJSON。

分批处理,别一次占满一帧

必须在主线程算时,把任务切成小块,每块结束让出一帧。 这样地图的拖动与缩放不会被长时间阻塞。

function processInBatches(items, size, handle, done) {
  var i = 0;

  function step() {
    var end = Math.min(i + size, items.length);
    for (; i < end; i++) handle(items[i], i);

    if (i < items.length) {
      requestAnimationFrame(step);
    } else {
      done();
    }
  }

  step();
}

processInBatches(samples, 500, function (pt) {
  pt.properties.near = turf.pointToLineDistance(pt, route, {
    units: 'kilometers'
  });
}, function () {
  console.log('全部完成');
});

mutate 与 clone 的取舍

默认情况下函数返回新对象,十万次调用就是十万份深拷贝。 确定不再需要原数据时,传 mutate: true 能显著减少分配。

做法开销风险
默认(不 mutate)每次深拷贝入参内存峰值高,GC 频繁
mutate: true原地修改原始数据被改写,无法回退
先 turf.clone 再 mutate一次拷贝 + 原地改只适合需要保留一份副本时

注意事项

在渲染回调里调用 Turf

map.on('mousemove') 或动画循环里做 buffer、 intersect,会把帧率拖到个位数。这类计算按事件结束触发, 或加防抖到 100 毫秒以上。

用 clone 代替构造

需要给每个点补一个属性时,turf.clone(feature) 再改, 比新建 turf.point(coord, props) 慢。坐标本来就来自原数据, 直接构造新要素更省。

忽视结果的几何类型

大批量 intersect 中,有些返回 null、 有些返回 GeometryCollection。直接喂给渲染层会让整批失败。 批量处理时按 result && result.geometry 过滤。

引用本文

经纬志(2026)。《性能与大数据》。https://www.jingweizhi.com/turf/tutorial/performance.html(访问日期 2026-11-03)。