CesiumJS 初始化、资源加载与避坑

版本基线:CesiumJS 1.133.1(核对日期:2026-08-27);文中通用 API 链接默认指向官方最新版本。

范围说明:本文只介绍 CesiumJS 官方公开 API、浏览器加载规则和通用构建方法,不依赖任何业务组件、私有 SDK 或二次封装。示例中的路径和令牌均为占位值。


1. 初始化结论与推荐顺序

稳定初始化的关键不是"尽快 new Viewer",而是先确定唯一运行时、固定资源版本、配置资源基路径,再并行启动官方 Provider 请求。地形选择确定后只创建一次 Viewer,并把"Provider 可用""Viewer 已构造"和"当前视野瓦片已加载"视为三个不同阶段。

|-------------|---------------------------------------------|-------------------------------------------------------------------|
| 阶段 | 操作 | 官方 API 或检查点 |
| 1. 运行时 | 全局预构建版或 npm/ESM 二选一;保持 JavaScript 与静态资源版本一致 | Cesium.VERSION |
| 2. 资源定位 | 在首次资源请求前设置基路径,并加载 Widgets 样式 | CESIUM_BASE_URL、Cesium.buildModuleUrl() |
| 3. 鉴权 | 在发起 Cesium ion 请求前写入访问令牌 | Cesium.Ion.defaultAccessToken |
| 4. Provider | 尽早并行创建地形与影像 Provider | Cesium.createWorldTerrainAsync()、Cesium.createWorldImageryAsync() |
| 5. Viewer | 地形方案确定后一次性创建 Viewer | new Cesium.Viewer()、new Cesium.ImageryLayer() |
| 6. 就绪观测 | 区分 Provider 就绪、Viewer 构造完成与当前视野瓦片完成 | tilesLoaded、tileLoadProgressEvent |
| 7. 清理 | 移除监听并销毁 WebGL、DOM 与事件资源 | viewer.isDestroyed()、viewer.destroy() |

必须遵守的四条规则

· 同一页面只加载一种 CesiumJS 运行时,不同时使用全局 Cesium.js 和 npm/ESM 运行时。

· Cesium.js、Workers、ThirdParty、Assets、Widgets 与 npm 包必须来自同一发行版本。

· CESIUM_BASE_URL 必须在 CesiumJS 第一次解析 Workers、Assets 或 Widgets 资源之前生效。

· Viewer 销毁后,除 isDestroyed() 外,不再调用该实例的其他属性或方法。


2. 版本与静态资源一致性

本文以 1.133.1 为兼容基线。查阅 API 时优先使用 1.133 版本固定文档,并结合 1.133.1 Release 与 CHANGELOG;不要把 latest 页面的示例未经核对直接套用到旧版本。升级前应在测试环境确认 API、Workers、地形与影像加载行为,并确保 Cesium.js、Workers、ThirdParty、Assets、Widgets 原子发布且版本一致。

运行时需要的目录

· Workers:Web Worker 脚本。缺失或路径错误时,地形解析、几何处理等任务会失败。

· ThirdParty:预构建运行时所需的第三方依赖资源。

· Assets:近似地形高度、纹理等运行时资源。

· Widgets:控件图片、字体与 widgets.css。

避坑:页面能看到地球不代表资源完整。应在浏览器网络面板确认 Workers、Assets、Widgets 等请求没有 404,并通过 Cesium.VERSION 与发布包版本进行核对。


3. 全局预构建版加载

全局预构建版适合通过静态目录直接部署。加载顺序固定为:先声明 CESIUM_BASE_URL,再加载同版本 widgets.css 与 Cesium.js,最后执行应用入口;完整顺序见第 5 节。defer 脚本应保持文档顺序,普通脚本则应放在依赖已加载的位置。

· CESIUM_BASE_URL 可以是绝对路径或相对路径,但必须指向同时包含四类运行时目录的位置。

· 不要依赖脚本下载完成的偶然时序;使用 defer 顺序或在明确的 load 事件后启动。

· 不要重复插入 Cesium.js。重复运行时会导致类型判断、事件对象和资源缓存不一致。


4. npm / ESM 加载

ESM 模式下从 cesium 包导入官方模块,并导入 Widgets 样式。构建产物仍必须能访问 Workers、ThirdParty、Assets、Widgets。基路径应由构建配置定义,或在模块图开始执行前由页面声明。

import * as Cesium from "cesium";

import "cesium/Build/Cesium/Widgets/widgets.css";

console.info(`CesiumJS ${Cesium.VERSION}`);

构建阶段检查

· 固定 cesium 依赖版本;不要让锁文件与部署静态资源来自不同版本。

· 把 Workers、ThirdParty、Assets、Widgets 复制到可公开访问的同一基目录。

· 让 CESIUM_BASE_URL 在生产环境的子路径、CDN 前缀和本地开发路径下都能解析。

· 不要为了调用某个全局扩展而再加载 Cesium.js;需要全局引用时,可明确赋值 globalThis.Cesium = Cesium,但页面仍只能有一个运行时实例。

选择原则:全局预构建版和 ESM 版没有"谁更快"的固定答案。优先选择与现有构建链一致、能保证版本和静态资源原子发布的方式。


5. Cesium ion 与异步 Provider 初始化

Cesium ion 的地形与全球影像需要访问令牌。先设置 Cesium.Ion.defaultAccessToken,再并行调用 Cesium.createWorldTerrainAsync() 与 Cesium.createWorldImageryAsync();第 5 节完整示例统一处理成功、失败与降级路径。

可直接运行的完整示例(CesiumJS 官方 API + Web 标准 API)

<!doctype html>

<html lang="zh-CN">

<head>

<meta charset="UTF-8" />

<meta name="viewport" content="width=device-width, initial-scale=1.0" />

<title>CesiumJS 1.133.1</title>

<script>

window.CESIUM_BASE_URL = "/Cesium/";

</script>

<link rel="stylesheet" href="/Cesium/Widgets/widgets.css" />

<style>

html, body, #cesiumContainer {

width: 100%;

height: 100%;

margin: 0;

overflow: hidden;

}

</style>

<script src="/Cesium/Cesium.js"></script>

</head>

<body>

<div id="cesiumContainer"></div>

<script>

(async () => {

const Cesium = globalThis.Cesium;

if (!Cesium) throw new Error("CesiumJS runtime is unavailable");

console.info(`CesiumJS ${Cesium.VERSION}`);

Cesium.Ion.defaultAccessToken = "YOUR_ION_TOKEN";

// Web 标准 API:Promise.allSettled()。

// CesiumJS 官方 API:下列 Provider 创建函数与 Provider 类型。

const terrainResult, imageryResult = await Promise.allSettled([

Cesium.createWorldTerrainAsync({

requestVertexNormals: false,

requestWaterMask: false,

}),

Cesium.createWorldImageryAsync({

style: Cesium.IonWorldImageryStyle.AERIAL,

}),

]);

if (terrainResult.status === "rejected") {

console.warn("World terrain unavailable", terrainResult.reason);

}

if (imageryResult.status === "rejected") {

console.warn("World imagery unavailable", imageryResult.reason);

}

const terrainProvider =

terrainResult.status === "fulfilled"

? terrainResult.value

: new Cesium.EllipsoidTerrainProvider();

const baseLayer =

imageryResult.status === "fulfilled"

? new Cesium.ImageryLayer(imageryResult.value)

: false;

const viewer = new Cesium.Viewer("cesiumContainer", {

terrainProvider,

baseLayer,

animation: false,

timeline: false,

baseLayerPicker: false,

geocoder: false,

infoBox: false,

scene3DOnly: true,

});

viewer.camera.flyTo({

destination: Cesium.Cartesian3.fromDegrees(

116.3913,

39.9075,

1500

),

});

})().catch((error) => {

console.error("CesiumJS initialization failed", error);

});

</script>

</body>

</html>

Cesium.createWorldTerrainAsync() 返回 Promise<CesiumTerrainProvider>,Cesium.createWorldImageryAsync() 返回 Promise<IonImageryProvider>。Promise 完成表示 Provider 实例已创建,不表示当前视野的地形和影像瓦片已经加载。Cesium.ImageryLayer.fromProviderAsync() 可接收 ImageryProvider Promise,并在 Provider 就绪后开始渲染,同时通过图层事件报告异步错误。Promise.allSettled() 属于 Web 标准 API。

避免首帧重建:如果最终要使用世界地形,先等待地形 Provider,再创建 Viewer。先显示椭球地形、随后替换为世界地形会造成可见跳变、重复请求和额外场景状态迁移。

Terrain.fromWorldTerrain() 的替代写法

Cesium.Terrain.fromWorldTerrain() 返回 Terrain 实例,可传给 Viewer 的 terrain 选项;仅当 terrainProvider 未设置时才能使用 terrain。Terrain.readyEvent 在 TerrainProvider 创建成功时触发,Terrain.errorEvent 在异步创建出错时触发;readyEvent 触发前不要读取 Terrain.provider。

const viewer = new Cesium.Viewer("cesiumContainer", {

terrain: Cesium.Terrain.fromWorldTerrain(),

});


6. Viewer 选项与最小界面

Viewer 默认会启用多种控件和数据源能力。应按产品需要显式配置,避免依赖版本升级后可能变化的默认表现。下表只列出 Viewer 的官方构造选项。

|---------------------------------|----------------------------|---------------------------------|
| 选项 | 用途 | 建议 |
| terrainProvider / terrain | 初始地形 | 二选一;需要显式错误处理时使用 terrainProvider |
| baseLayer | 初始底图图层 | 可传 ImageryLayer;不需要底图时传 false |
| animation、timeline | 时间控制组件 | 非时间序列场景通常关闭 |
| baseLayerPicker | 底图与地形选择器 | 固定数据源时关闭 |
| geocoder | 地理编码搜索 | 未提供搜索工作流时关闭 |
| homeButton | 默认视角按钮 | 需要自定义首页视角时评估是否保留 |
| sceneModePicker | 2D / 3D / Columbus View 切换 | 纯三维应用可关闭 |
| navigationHelpButton | 导航帮助 | 已有独立帮助入口时关闭 |
| fullscreenButton | 全屏控件 | 容器受布局约束时按需开启 |
| infoBox、selectionIndicator | 实体选择反馈 | 不使用 Entity 选择交互时关闭 |
| scene3DOnly | 只创建三维场景所需资源 | 确定不切换场景模式时设为 true |
| requestRenderMode | 仅在需要时渲染 | 静态或低频更新场景可开启 |
| maximumRenderTimeChange | 时间变化触发渲染的最大间隔 | 有时钟驱动内容时谨慎调整 |
| useBrowserRecommendedResolution | 使用浏览器建议分辨率 | 高 DPI 设备优先测试该选项 |

版权与署名:不要通过隐藏 creditContainer、移动署名到不可见区域或覆盖样式来移除 Cesium 及数据提供方的版权信息。任何定制都必须符合 CesiumJS、Cesium ion 和数据提供方的许可与署名条款。


7. 就绪边界、进度与错误

CesiumJS 初始化没有一个能够代表全部完成的单一 Promise。应根据业务真正依赖的阶段选择检查点。相机移动、图层变化或细节层级变化都会产生新的瓦片请求。

|-------------|--------------------------------------------------|----------------------------------------------------------|
| 边界 | 含义 | 可用检查点 |
| 运行时可用 | CesiumJS 已执行,官方命名空间存在 | globalThis.Cesium、Cesium.VERSION |
| Provider 可用 | CesiumTerrainProvider 或 IonImageryProvider 实例已创建 | 等待 createWorldTerrainAsync() / createWorldImageryAsync() |
| Viewer 已构造 | 场景、相机、控件与渲染循环已建立 | new Cesium.Viewer() 返回 |
| 当前视野瓦片完成 | 当前视野所需地形和影像队列暂时清空 | globe.tilesLoaded、tileLoadProgressEvent |
| 渲染失败 | 渲染循环捕获到异常 | scene.renderError |

const removeTileProgress =

viewer.scene.globe.tileLoadProgressEvent.addEventListener((pending) => {

if (pending === 0 && viewer.scene.globe.tilesLoaded) {

console.info("当前视野瓦片已加载");

}

});

const removeRenderError =

viewer.scene.renderError.addEventListener((scene, error) => {

console.error("Cesium render error", error);

});

·tilesLoaded 只描述当前视野中的地形与影像,不代表整个地球或未来视角已缓存。

·tileLoadProgressEvent 的参数是当前瓦片队列长度;相机持续移动时数值可以再次增大。

·renderError 适合记录渲染异常,但不能替代网络请求、令牌状态和 Provider Promise 的错误处理。


8. 相机、容器尺寸与显式渲染

初始相机定位

viewer.camera.flyTo({

destination: Cesium.Cartesian3.fromDegrees(

116.3913,

39.9075,

1500

),

complete: () => console.info("Camera flight completed"),

cancel: () => console.info("Camera flight cancelled"),

});

flyTo() 会启动异步飞行动画,但不返回 Promise。若页面必须区分飞行完成与取消,应使用官方 complete 和 cancel 回调;不要用固定 setTimeout 猜测动画结束时间。

容器尺寸变化:浏览器标准 API 与 CesiumJS 官方 API

ResizeObserver 是浏览器标准 API,不属于 CesiumJS。CesiumJS 1.133 官方文档说明 viewer.resize() 会按需自动调用;仅当 useDefaultRenderLoop 为 false 时不会自动调用。若还要监听容器本身的尺寸变化,可在 ResizeObserver 回调中调用 viewer.resize();开启 requestRenderMode 时,再调用 viewer.scene.requestRender()。

const resizeObserver = new ResizeObserver(() => {

if (!viewer.isDestroyed()) {

viewer.resize();

viewer.scene.requestRender();

}

});

resizeObserver.observe(viewer.container);


9. 性能设置:先测量,再调整

CesiumJS 的性能瓶颈可能来自请求延迟、瓦片解码、地形复杂度、屏幕像素数、实体数量或持续动画。不要用一组固定参数覆盖所有设备。先记录 Cesium.VERSION、视口尺寸、像素比、相机状态和网络条件,再逐项验证。

按需渲染

· requestRenderMode 适合低频更新场景;外部状态改变但 CesiumJS 无法感知时,需要调用 scene.requestRender()。

· maximumRenderTimeChange: Infinity 会停止因时间流逝而自动请求新帧。存在时钟动画、动态材质或时间变化数据时不要盲目设置。

分辨率与帧率

viewer.resolutionScale = 1.0;

viewer.targetFrameRate = 30;

· useBrowserRecommendedResolution 是 Viewer 构造选项;resolutionScale 是 Viewer 属性。两者应结合目标设备实测。

· 降低 resolutionScale 可以减少像素填充压力,但会降低画面清晰度。

· Viewer.targetFrameRate 是 Viewer 属性,仅在 useDefaultRenderLoop 为 true 时生效。未设置时由浏览器 requestAnimationFrame 决定帧率;设置值必须大于 0,且高于底层 requestAnimationFrame 上限不会产生额外效果。


10. 生命周期与完整清理

单页应用切页、组件卸载、容器替换或重新登录时,都应执行对称清理。事件监听、ResizeObserver 和 Viewer 必须由创建它们的生命周期负责释放。

function disposeCesium() {

removeTileProgress();

removeRenderError();

resizeObserver.disconnect();

if (!viewer.isDestroyed()) {

viewer.destroy();

}

}

· Event.addEventListener() 返回的移除函数应保存并调用。

· destroy() 会释放 WebGL 和相关对象;调用后不要继续读取 scene、camera、entities 等属性。

· 需要判断销毁状态时,调用 isDestroyed();这是 destroy() 后唯一允许调用的方法。


11. 常见故障矩阵

|------------------------|----------------------------------|------------------------------------------------------|
| 现象 | 优先检查 | 处理方式 |
| 页面空白或 Worker 404 | CESIUM_BASE_URL 的设置时机与最终 URL | 在运行时加载前设置基路径;确认 Workers 等目录可访问 |
| 控件图标或样式缺失 | widgets.css 与 Widgets 资源 | 加载同版本样式,并确认字体、图片请求没有 404 |
| ion 返回 401 / 403 | 令牌是否在 Provider 请求前设置,权限与域名限制 | 修正 Ion.defaultAccessToken 与令牌访问范围 |
| 地形或影像创建失败 | Provider Promise 的拒绝原因 | 分别捕获 Promise;必要时使用 EllipsoidTerrainProvider 降级 |
| 首帧出现地形跳变 | 是否先创建 Viewer 后替换 terrainProvider | 先确定地形 Provider,再创建 Viewer |
| 同页行为不稳定或 instanceof 异常 | 是否同时加载全局版与 ESM 版 | 只保留一个运行时,并统一所有导入来源 |
| 开发正常、生产资源 404 | 部署子路径、CDN 前缀与基路径 | 让 CESIUM_BASE_URL 与实际发布目录一致 |
| 加载进度反复变化 | 相机、视口或图层是否在变化 | 把进度解释为当前视野队列,不作为全局一次性完成标记 |
| 销毁后仍报错 | 异步回调与事件监听是否仍在访问 Viewer | 先移除监听和 Observer,再调用 destroy() |
| 高 DPI 设备卡顿 | 分辨率、像素比与填充压力 | 测试 useBrowserRecommendedResolution 与 resolutionScale |


12. 验收清单

· 运行时模式唯一:全局预构建版与 npm/ESM 没有同时存在。

· Cesium.VERSION 与 Cesium.js、Workers、ThirdParty、Assets、Widgets 的发行版本一致。

· CESIUM_BASE_URL 在首次 CesiumJS 资源解析前生效,生产子路径下无 404。

· widgets.css 已加载,控件、字体和图标显示正常。

· Ion.defaultAccessToken 在所有 ion Provider 请求前设置,权限遵循最小化原则。

· 地形与影像 Provider 的 Promise 均有明确错误处理或降级策略。

· Viewer 只创建一次;没有为了切换最终地形而重建首帧。

·"Provider 就绪""Viewer 已构造""当前视野瓦片完成"没有混为同一个加载状态。

· 相机定位使用 flyTo() 的 complete / cancel 回调,不用固定延时猜测完成。

· 容器尺寸变化后能正确 resize();按需渲染时会 requestRender()。

· 卸载时移除全部监听与 Observer,并在未销毁时调用 viewer.destroy()。

· Cesium 与数据提供方署名可见,符合相关许可条款。


13. 官方参考

以下 API 链接均固定到 CesiumJS 1.133 官方参考文档;latest API Reference 与 Quickstart 仅用于对照和入门。补丁版本差异以 1.133.1 Release 与 CHANGELOG 为准。

· CesiumJS API Reference(latest,仅用于对照):https://cesium.com/learn/cesiumjs/ref-doc/

· Viewer(1.133):https://cesium.com/downloads/cesiumjs/releases/1.133/Build/Documentation/Viewer.html

· Ion(1.133):https://cesium.com/downloads/cesiumjs/releases/1.133/Build/Documentation/Ion.html

· createWorldTerrainAsync(1.133):https://cesium.com/downloads/cesiumjs/releases/1.133/Build/Documentation/global.html#createWorldTerrainAsync

· createWorldImageryAsync(1.133):https://cesium.com/downloads/cesiumjs/releases/1.133/Build/Documentation/global.html#createWorldImageryAsync

· ImageryLayer(1.133):https://cesium.com/downloads/cesiumjs/releases/1.133/Build/Documentation/ImageryLayer.html

· Terrain(1.133):https://cesium.com/downloads/cesiumjs/releases/1.133/Build/Documentation/Terrain.html

· Globe(1.133):https://cesium.com/downloads/cesiumjs/releases/1.133/Build/Documentation/Globe.html

· Scene(1.133):https://cesium.com/downloads/cesiumjs/releases/1.133/Build/Documentation/Scene.html

· Camera(1.133):https://cesium.com/downloads/cesiumjs/releases/1.133/Build/Documentation/Camera.html

· Event(1.133):https://cesium.com/downloads/cesiumjs/releases/1.133/Build/Documentation/Event.html

· CesiumJS Quickstart(latest,仅用于入门):https://cesium.com/learn/cesiumjs-learn/cesiumjs-quickstart/

· CesiumJS 1.133 API Reference(版本固定):https://cesium.com/downloads/cesiumjs/releases/1.133/Build/Documentation/index.html

· CesiumJS 1.133.1 Release:https://github.com/CesiumGS/cesium/releases/tag/1.133.1

· CesiumJS 1.133.1 CHANGELOG:https://github.com/CesiumGS/cesium/blob/1.133.1/CHANGES.md

相关推荐
PYB31 天前
【Web·基础学习】基础布局
css·html·web
用户298698530141 天前
Python 文档格式转换实战:RTF 转 PDF 与 HTML
python·html·api
志尊宝7 天前
Vue3 零基础每日笔记(055):组件里正确使用 store——storeToRefs 解构与 $patch 批量修改
笔记·vue·html·前端开发·软件开发
吴声子夜歌7 天前
HTML——看似普通的元素的背后(<a>标签)
html
IMPYLH7 天前
HTML 的 <style> 元素
前端·html
刃神太酷啦7 天前
前端入门第一课:HTML 基础语法 + 常用标签 + 实战全解
服务器·c语言·前端·javascript·css·c++·html
IMPYLH7 天前
HTML 的 <strong> 元素
前端·html
开开心心就好7 天前
批量提取PDF中的图片,直接导出原图
前端·javascript·支持向量机·智能手机·pdf·html·启发式算法
我命由我123458 天前
CSS - CSS 媒体查询 orientation
前端·javascript·css·html·css3·html5·js