
简介面向Vue3与Cesium集成开发者的实操资源包解决在Vue3.x项目中快速接入Cesium并完成三维地图应用构建的工程化问题。资源共有85个文件核心包含Vue组件、JavaScript脚本、JSON配置与HTML入口另有Markdown说明和PNG图片辅助理解压缩包仅347KB便于下载后对照学习。目前已有5113人学习下载。内容提供完整的Vue工程目录结构涵盖组件、路由、状态管理等模块并包含CesiumViewer组件封装示例、vue.config.js路径别名配置、package.json依赖清单等关键文件可帮助开发者了解从项目创建、Webpack调整、Cesium初始化到组件化封装、事件交互、性能优化的全链路实现。文件包内还保留Git对象数据适合排查集成过程中遇到的版本或配置问题能为地理信息可视化、智慧城市等场景的开发提供直接的代码参考。 做Web三维GIS有一段年头了这两年用 Vue3.x 接 Cesium 的咨询越来越多。很多朋友其实不是被 Cesium 本身的 API 难住而是第一步就把工程环境搞乱了有人还在用 Vue CLI 硬扛有人把 Cesium 当成普通 npm 包直接 import打完包一堆资源 404半天找不到原因。这篇文章就把我从零到一用 Vue3.x Cesium 做项目的完整经验整理出来包括环境初始化、核心概念、高频需求落地和踩坑记录。适合已经会用 Vue3 语法、正准备做三维地图或数字孪生大屏的开发者参考内容可以直接抄进自己的项目里。1. 环境准备Vue3 Vite Cesium 初始化1.1 为什么我放弃了 Vue CLI 改用 ViteCesium 是一个体积非常庞大的运行时库包含大量 Web Worker、wasm、纹理、widgets 等静态资源。早期的 Vue CLI Webpack 方案也能跑但每次配置 loaders、copy plugin、publicPath 都很啰嗦碰上新版本还要改配置。Vite 基于 esbuild 预构建开发调试速度快模块多了以后热更新体验差别非常明显所以这几年新项目我基本默认 Vite。Cesium 官方并没有提供 Vite 插件目前社区方案主要靠vite-plugin-cesium来自动处理资源拷贝和路径注入也有一些项目会手工配置。个人项目或简单 demo 可以直接用社区插件但商业项目我建议手工控制资源拷贝因为插件迭代频率不稳定遇到升级容易出现隐藏问题。1.2 Cesium 的安装与静态资源处理先创建一个 Vite Vue3 项目然后安装 Cesium。npm create vitelatest vue-cesium-demo -- --template vue cd vue-cesium-demo npm install npm install cesium装完之后注意看node_modules/cesium/Build/Cesium这个目录里面有Cesium.js、Workers、Assets、Widgets、ThirdParty等子目录。Cesium 在运行时需要通过CESIUM_BASE_URL去动态加载这些静态文件如果路径不对浏览器控制台会报各种资源加载失败甚至直接白屏。我个人最常用的方案是把整个Build/Cesium目录复制到项目的public/cesium然后再手动指定全局变量!-- index.html -- script window.CESIUM_BASE_URL /cesium /script如果使用社区插件Vite 配置会简洁很多import { defineConfig } from vite import vue from vitejs/plugin-vue import cesium from vite-plugin-cesium export default defineConfig({ plugins: [vue(), cesium()] })然后在组件里初始化 viewerimport * as Cesium from cesium Cesium.Ion.defaultAccessToken 你的 token const viewer new Cesium.Viewer(cesiumContainer)这里我想强调一个比较隐蔽的坑如果你的项目部署在服务器子目录下CESIUM_BASE_URL写死/cesium就会出问题。更稳的做法是用 Vite 的import.meta.env.BASE_URL动态拼接比如window.CESIUM_BASE_URL import.meta.env.BASE_URL cesium这样不管部署在根路径还是子目录资源路径都能对上。1.3 Ion Token 到底要不要配Cesium Ion 是官方提供的在线影像、地形、3D Tiles 托管服务。只要使用在线资源就必须设置Cesium.Ion.defaultAccessToken。如果项目纯离线、全部用自己的瓦片服务和本地模型token 可以暂时不配但官方沙盒里很多示例默认走 Ion 资源直接复制代码到本地可能加载不出来需要先明白这一点。token 的管理我也踩过坑。初期图省事直接写在代码里后来发现有同事把仓库推到公开仓库token 跟着泄露了。建议把 token 放到环境变量文件里并且在 Ion 后台限制这个 token 能访问的 asset 权限生产环境不要用管理权限的 token。2. Vue3 工程里的 Cesium 基础概念2.1 Viewer 和 Scene 到底在操作什么很多新手分不清viewer和scene这两个对象。简单理解Viewer是带 UI 外壳的入口封装了底图、时间轴、动画控件、图层管理这些基础设施Scene是真正的三维渲染核心负责管理相机、模型、图元、光照效果。日常写代码70% 的时间都在操作viewer.scene和viewer.camera。在 Vue3 组件里初始化 Cesium 时要特别注意组件的生命周期。容器必须在 DOM 渲染完成后才能创建 viewer组件卸载前要手动销毁否则会留下定时器和 WebGL 上下文页面切换多了会越来越卡。import * as Cesium from cesium let viewer null onMounted(() { viewer new Cesium.Viewer(cesiumContainer, { animation: true, baseLayerPicker: true, timeline: true }) }) onBeforeUnmount(() { viewer?.destroy() viewer null })还有一个 Vue3 特有的问题不要盲目把 Cesium 的大对象塞进ref或reactive里。Cesium 内部对象数量多、结构复杂交给 Vue 的响应式代理后性能开销非常大而且容易引起循环引用警告。正确的姿势是只把需要响应式驱动 UI 的少量数据放到ref里比如当前经纬度、相机高度、选中实体名称等。2.2 Entity、Primitive 和 DataSource 怎么选Cesium 提供多种数据承载方式选错后面会很难受。Entity 是面向业务的高级封装API 直观适合点、线、面、模型、标签等中小规模数据开发效率最高。Primitive 是底层图元需要自己管理几何实例和外观但性能好适合海量点位、大规模建筑物、动态雷达波这类场景。DataSource 则负责统一管理 GeoJSON、KML、CZML 等外部数据格式一般用于数据文件动态加载。我自己的判断标准是如果数据量在几百上千个对象用 Entity 完全没问题如果要做十万级点云或建筑批量渲染就直接上 Primitive 或 3D Tiles。Cesium 社区经常流传一句话“Entity 是给业务用的Primitive 是给性能用的”确实如此。2.3 官方文档和中文文档怎么看Cesium 的官方 API 文档和沙盒Sandcastle都是英文的中文文档基本靠社区维护版本滞后是常态。我的经验是直接看官方沙盒里的可运行示例比翻二手教程准确得多。看中文资料时一定要确认版本号否则经常遇到 API 在最新版本已经废弃或改名的尴尬情况。官方文档建议优先看三个板块Viewer、Scene、Entity。沙盒里每个示例都有完整代码左上角可以切换查看源码和运行效果遇到某个材质或图形不会写直接在沙盒里搜索关键词通常比搜索引擎更快。3. 高频需求实操图形、材质与模型加载3.1 绘制矩形、圆柱体和高德箭头效果Cesium 画矩形非常简单关键是理解Rectangle.fromDegrees的参数顺序西、南、东、北对应经度范围 116.2 到 116.5、纬度范围 39.8 到 40.1。viewer.entities.add({ rectangle: { coordinates: Cesium.Rectangle.fromDegrees(116.2, 39.8, 116.5, 40.1), material: Cesium.Color.YELLOW.withAlpha(0.5), height: 0 } })绘制带箭头的线Cesium 内置了PolylineArrowMaterialPropertyviewer.entities.add({ polyline: { positions: Cesium.Cartesian3.fromDegreesArray([116.2, 39.8, 116.4, 40.0, 116.6, 39.9]), width: 8, material: new Cesium.PolylineArrowMaterialProperty(Cesium.Color.CYAN) } })但想要实现高德地图那种有渐变、有圆角、带尾迹的箭头效果内置材质就不够用了。社区常见的方案是用PolylineVolume构建立体管道配合自定义贴图材质或者用 CatmullRomSpline 把点列平滑成曲线再叠加自定义线材质模拟动态流光。思路是先确定箭头主体轨迹再在材质里加方向、速度、颜色渐变这几个 uniform实际做出来的效果已经比较接近高德了。圆柱体在 Entity 里也有直接属性空心圆柱则需要绕一下viewer.entities.add({ cylinder: { length: 400, topRadius: 100, bottomRadius: 100, material: Cesium.Color.CYAN.withAlpha(0.6), outline: true } })Cesium 没有直接提供“空心圆柱”的 Entity 接口。最容易理解的做法是外圆柱和内圆柱叠加视觉上形成空心。如果想要真正的几何空心就需要用CylinderGeometry自定义 Primitive或者用PolygonGeometry的extrudedHeight从底面挤出中空墙体。我的建议是业务上只是看效果就两层圆柱叠加涉及剖切、断面分析就必须做几何级空心。3.2 自定义材质河流、雷达、动态光照Cesium 材质系统也是很多人刚接触时容易懵的地方。其实核心就是 Fabric 材质定义写一个 shader 的czm_getMaterial函数控制颜色和透明度输出。以动态河流材质为例核心思路是让纹理沿着materialInput.st坐标随时间流动Cesium.Material._materialCache.addMaterial(RiverMaterial, { fabric: { type: RiverMaterial, uniforms: { color: new Cesium.Color(0.1, 0.6, 0.9, 0.8), speed: 1.0 }, source: czm_material czm_getMaterial(czm_materialInput materialInput) { czm_material material czm_getDefaultMaterial(materialInput); float time czm_frameNumber * 0.01 * speed; float alpha fract(materialInput.st.x * 3.0 time); material.diffuse color.rgb; material.alpha color.a * alpha; return material; } } })雷达光波效果思路类似只不过采样从直线纹理变成圆形区域让透明度和颜色沿着半径方向扩散或收缩再叠加时间变量就能看到一圈一圈往外发射的雷达波。动态光照如果只是作用于模型或 3D Tiles 表面可以用 Cesium 的CustomShader实现不必去改模型源文件。可以控制发光强度、闪烁频率、颜色渐变适合做数字孪生里的设备状态提示。注意 CustomShader 是内置在渲染管线里的性能比逐帧改材质要好不少。3.3 模型加载、模型节点和 LOD 策略加载本地 glTF 模型也比较直接viewer.entities.add({ position: Cesium.Cartesian3.fromDegrees(116.39, 39.9), model: { uri: /models/CesiumMan.gltf, scale: 1.0, heightReference: Cesium.HeightReference.CLAMP_TO_GROUND } })关于“模型节点”很多朋友问怎么隐藏或者移动 glTF 内部的某个零部件。这里要注意Cesium 官方没有把模型节点操作做成稳定 API网上有些代码用model._getNodeByName这种私有接口调试时可以跑上生产非常危险。我们实际项目中需要做零部件级控制的模型都会在建模阶段拆分成多个独立 glTF 文件然后在代码里分别加载、分别控制。如果想动态换色或闪烁优先用 CustomShader而不是去改模型结构。高斯泼溅3D Gaussian Splatting模型在 Cesium 里显示也是最近的热门问题。目前官方没有稳定接口社区有一些实验性插件但大场景性能还不乐观。如果只是尝鲜可以考虑把 splat 数据预烘焙成点云或自定义渲染器再挂到 Cesium 场景里不建议依赖在正式项目里。关于热词“cesium相机周边加载低精度”其实就是 LOD。3D Tiles 会根据相机距离和屏幕空间误差自动选择加载不同精度的瓦片控制参数是tileset.maximumScreenSpaceError调大一点会更快切换到低精度模型加载流畅但细节丢失调小一点更精细但请求数会暴涨。如果用的是普通 glTF 模型Cesium 不会自动切 LOD需要自己在业务里根据相机距离切换不同精度的模型文件。3.4 底图、MVT 和热力图图片当底图常见需求是把一张平面设计图或 CAD 导出的图贴在地球表面viewer.imageryLayers.addImageryProvider( new Cesium.SingleTileImageryProvider({ url: /map.png, rectangle: Cesium.Rectangle.fromDegrees(116, 39, 117, 40) }) )这里要注意给图片设置正确的rectangle否则图片会默认覆盖全球范围拉伸到你怀疑人生。MVTMapbox Vector Tile格式在 Cesium 里没有原生支持。我目前的方案有两种一种是在服务端把 MVT 动态转成 GeoJSON再用GeoJsonDataSource加载另一种是前端用geojson-vt把矢量数据切片再按瓦片渲染成图片接入自定义 ImageryProvider。实际项目中如果 MVT 图层只是底图辅助我更建议在服务端转换前端解析 pbf 的 CPU 开销不低。热力图也属于高频需求。通用做法是先用 heatmap.js 把数据渲染到 canvas再把 canvas 转成图片作为图层叠加上去。数据更新时重新生成图片替换图层即可。如果数据量大或者要求实时交互就需要自己做 Canvas 纹理和 Cesium 渲染的对接这块建议先跑通静态版本再优化。4. 几个容易绕路的进阶玩法4.1 可视域分析和雷达扫描可视域分析听起来很高级核心其实就是射线求交。从观察点沿某个方向发射一条Cesium.Ray用scene.pickFromRay找到第一个遮挡点再比较遮挡点距离和观察点到目标点的距离就能判断目标是否可见。逐条射线扫描一圈就能得到可视区域边界。实际项目里射线数量多建议放到 Web Worker 里算避免阻塞主线程。雷达扫描效果和可视域分析经常被放到一起。雷达波的“扇形扫过”效果可以用多边形 材质渐变实现也可以在前面说的动态材质里加一个从初始角度到结束角度的旋转 uniform。如果想要那种一圈圈扩散的雷达光波本质上是在圆形范围内做透明度和颜色的径向渐变加上时间变量循环播放。4.2 视频贴图和图片底图Cesium 支持把 video 元素直接作为材质图片使用这个特性用来做数字孪生大屏里的监控画面、设备运行状态、宣传动画非常方便const video document.createElement(video) video.src /monitor.mp4 video.loop true video.muted true video.play() viewer.entities.add({ rectangle: { coordinates: Cesium.Rectangle.fromDegrees(116.2, 39.8, 116.6, 40.1), material: video } })注意视频需要静音并允许自动播放否则在很多浏览器里会一直黑屏。真机上如果视频不显示优先检查浏览器自动播放策略。4.3 地形压平和 CGCS2000 定制“地形压平”这个词看起来简单实际上有歧义。如果只是想在某块区域让地形视觉上变平可以尝试裁剪多边形ClippingPolygon的方案对新版 Cesium 的地形和 3D Tiles 有一定支持。但要做到大规模、精确的压平我建议还是在地形数据生产阶段处理前端做复杂的地形编辑既不稳定性能也难保证。CGCS2000 定制主要是针对国内测绘数据。Cesium 默认基于 WGS84 椭球而 CGCS2000EPSG:4490与 WGS84 在多数场景下椭球参数差异极小关键问题在切片网格。最省事的做法是用天地图或者 GeoServer 发布的 EPSG:4490 标准瓦片服务用WebMapTileServiceImageryProvider接入。做数据对接时一定先确认源数据是经纬度坐标系还是投影坐标系混用会直接导致要素偏移。5. 遇到过的坑和排查方法5.1 打包后白屏、静态资源 404这是 Vue3 Cesium 最经典的问题。开发环境一切正常一执行npm run build部署上线就白屏控制台一堆Failed to load resource。原因基本都是 Cesium 的静态资源没有正确进入产物目录。排查思路很简单按这个顺序查确认CESIUM_BASE_URL是否指向了正确的绝对路径或部署相对路径确认public/cesium目录是否存在并且包含Workers、Assets、Widgets子目录确认项目是否部署在子路径CESIUM_BASE_URL有没有写死成/cesium打开浏览器 Network 面板看看第一个 404 请求是什么文件能直接定位问题。5.2 GroundPrimitive 强制更新和重绘问题GroundPrimitive是贴地的图元适合做贴地矩形、贴地多边形。但有很多人踩过“改了数据画面不刷新”的坑。Cesium 的 GroundPrimitive 在创建后直接修改geometryInstance的属性并不会触发 GPU 资源重建。我用下来的最稳妥做法是更新数据时先从scene.primitives里移除旧的 primitive然后创建新的 GroundPrimitive 再加进去简单粗暴但有效。如果业务里数据更新很频繁比如实时围栏、动态预警区域推荐把更新逻辑封装成一个函数统一处理 remove 和 add避免大量散落的代码导致漏移除。5.3 性能监控与相机 LOD 调节大场景卡顿是绕不开的话题。我一般会先开启两个配置viewer.scene.requestRenderMode true viewer.scene.maximumRenderTimeChange InfinityrequestRenderMode 的核心作用是场景没有变化时不重新渲染对静态三维大屏非常有效。配合相机移动才触发重绘CPU 和 GPU 占用能明显降下来。同时3D Tiles 的maximumScreenSpaceError是调节加载粒度最直接的参数默认一般是 16如果想要流畅优先我经常调到 24 到 32。Cesium 较新版本里还有性能偏好相关参数比如 3D Tiles 的请求调度和渲染偏好简单理解就是告诉引擎优先保帧率还是保精度不同业务按需调整。debugShowFramesPerSecond打开后可以在左上角看 FPS定位瓶颈时非常有用。5.4 编译 Cesium 分支有必要吗热词“编译cesium分支”应该指的是从源码编译自定义版本。Cesium 仓库是开放的理论上可以 clone 源码后修改再自己构建但这里我要泼一盆冷水如果不是要修改 Cesium 核心渲染逻辑不建议前端工程直接编译分支。原因很简单自己编译的产物和官方 npm 包结构不一致Worker 注册路径、资源加载方式都可能变化维护成本很高。更推荐的做法是在业务层封装工具函数、扩展Cesium.Material或者包装成 Vue 组件复用效果一样风险小得多。最后再分享一点个人体会Cesium 现在整个产品线迭代很快Unreal、Unity、CesiumJS 都在持续更新数据格式层面 3D Tiles 基本打通了大多数场景。但从前端工程的角度看CesiumJS 依然更像一个大型运行时而不是普通 npm 依赖部署时要考虑静态资源、Worker、底图服务这些工程化因素。新项目可以从官方沙盒复制一个最小示例然后在 Vue 组件里逐步迁移先跑通 viewer 创建、销毁、相机控制三部曲再叠加图形、材质和模型。遇到问题尽量直接看官方文档和社区英文 issue用关键词搜索得到的信息质量会高很多。本文还有配套的精品资源点击获取