经纬志
首页 / 实战手册 / 性能

首屏加载十几秒,用户等不及就关了

先看结论

用户打开页面,先看到空白背景几秒,再看到一堆灰块慢慢变成地图,最后才是数据。 每一段都在流失人。

最后验证 2026-11-03 · 多技术栈 · 更新 2026-10-27 · 引用本文

先分清是哪一段慢

不要笼统地说「加载慢」。用网络面板看时间线,通常是下面四段之一占了大头, 优化手段完全不同。

阶段表现优化方向
库下载白屏,什么都没有减体积、换 CDN、预加载
首屏瓦片灰块逐个变清晰降初始层级、限制并发、用低清占位
业务数据底图有了但点没出来减数据量、按视野请求、分页
渲染阻塞数据到了但页面卡住不动分批渲染、Web Worker、虚拟化

库体积:Cesium 是重灾区

Leaflet 压缩后约 42 KB,OpenLayers 完整包大很多,Cesium 的 Cesium.js 加资源接近 3 MB。三者的优化空间完全不同。

库约体积能做的
Leaflet42 KB基本到极限,别再多引插件
OpenLayers数百 KB按需引入模块,不要整包
Cesium约 3 MB用官方构建裁剪;首屏前加骨架屏

Cesium 首屏慢是物理限制,与其死磕下载时间,不如给用户一个明确的加载进度, 让人知道还要等多久。什么都没显示的空白比转圈更难受。

瓦片:别一上来就三级细节

初始缩放级别越高,首屏需要的瓦片就越多。把初始层级从 15 降到 12, 瓦片数量可能减少到八分之一。

var map = L.map('map', {
  center: [39.9, 116.4],
  zoom: 11,          // 先给一个粗略的层级
  minZoom: 3
});

另外可以限制并发请求数。并发太高会把带宽均摊到每张瓦片上,单张反而更慢, 首屏整体可见时间被拖长。

// OpenLayers
var map = new ol.Map({
  // ...
  maxTilesLoading: 8      // 默认 16,调低可让首屏瓦片更快出齐
});

数据:按视野请求,不要一次拉全量

最常见的错误是页面一加载就请求全国数据。正确做法是先拿到当前视野范围, 只请求这个范围内的数据,移动结束后再增量拉取。

function loadForViewport(map) {
  var b = map.getBounds();
  fetch('/api/points?bbox=' + [
    b.getWest(), b.getSouth(), b.getEast(), b.getNorth()
  ].join(','))
    .then(function (r) { return r.json(); })
    .then(render);
}

map.on('moveend', function () { loadForViewport(map); });
loadForViewport(map);

渲染:分批插入

一次性往地图里加两千个要素,主线程会被占住,页面卡住几百毫秒。 分批插入可以让浏览器有机会插进来响应用户操作。

function addInBatches(items, size) {
  var i = 0;
  function next() {
    var end = Math.min(i + size, items.length);
    for (; i < end; i++) {
      group.addLayer(makeMarker(items[i]));
    }
    if (i < items.length) {
      requestAnimationFrame(next);
    }
  }
  next();
}

优化的优先顺序

  1. 先量一下:网络面板里到底哪一段占了大头
  2. 数据全量拉取 → 改按视野请求,收益最大
  3. 初始层级过高 → 降到 10–12
  4. 一次性插入大量要素 → 改分批
  5. 库体积 → 按需引入或裁剪构建
  6. 最后再考虑骨架屏和加载进度,改善的是感受不是速度

先做能立刻看到的:加一个明确的加载提示层。 用户能接受等三秒,不能接受「不知道还要等多久」。 这一项几乎零成本,但对跳失率的影响往往比优化瓦片更大。

引用本文

经纬志(2026)。《首屏加载十几秒,用户等不及就关了》。https://www.jingweizhi.com/cookbook/first-screen-slow.html(访问日期 2026-11-03)。