
简介ViteVue3TypeScriptCesium.js集成模板是一份面向三维GIS与可视化场景的开箱即用脚手架整合了最新Vite构建工具、Vue3组合式API、TypeScript类型系统与Cesium.js三维渲染能力并预置axios、element-plus、vuex等主流依赖库帮助有一定前端基础的中高级开发者省去环境配置与依赖选型快速进入业务功能开发。整个模板压缩包仅21KB共16个文件以TypeScript源码.ts和Vue单文件组件.vue为代码主体配以JSON配置、SVG图标、CSS样式、HTML入口及YAML环境设置等文件形成清晰的工程目录骨架便于按模块检索和二次改造。资源内置一个可交互的基本地球场景示例演示Cesium API创建球体、视角切换与要素交互等关键操作并留有扩展接口可直接在其上叠加业务图层、实现三维量测或对接后端数据。目前已有2686人学习下载对正在选型CesiumVue3技术栈或需要快速搭建三维可视化原型的开发者是一份高性价比的起始模板。1. 引子为什么说“ViteVue3TSCesium”最难的是静态资源把 Cesium.js 集成进 Vite Vue3 TypeScript 工程真正卡人的不是 npm install而是静态资源与服务路径。某次我接手模拟项目X 的三维园区可视化前端栈是标准的 ViteVue3TS同事直接在 index.html 里用 script 标签引入了老版 Cesium。页面能打开但工具栏消失、影像层报 403、控制台全是 /Workers 目录 404。排了一个下午根源就一条Cesium 的 Worker、Widgets、Assets 是运行时按相对路径动态加载的CDN 引入和构建工具托管完全是两套逻辑。这篇安装笔记会把版本选型、目录结构、TypeScript 声明、资源搬运和常见翻车场景都展开照着走能搭出一个开箱即用的 ViteVue3TypeScriptCesium.js 集成模板适合第一次做三维球、城市级大屏或 GIS 业务的开发者。2. 版本选型与环境准备先把 Node 与四个核心库钉在同一个兼容面上Cesium 与 Vite 的版本错位是最隐蔽的坑。Cesium 1.1x 之后全面切到 ESM/CommonJS 双入口而 Vite 5/6 对 ESM 的解析、对 worker 的相对路径处理并不自动若拿一个 Vue2 webpack 的老工程强行迁移往往耗时半天也跑不起来。我建议先统一环境Node 18.17 或 20.19 以上 LTSVite 5/6 任选Vue3.4TypeScript 5.4Cesium 1.118 及以上。版本不要追最新追到与现有插件兼容的稳定段就行省得把时间耗在“升级后插件突然报错”的玄学问题上。2.1 检查本机 Node 与包管理器常见做法是先用 nvm 控制 Node 版本避免系统自带版本过低。我一般会先跑node -v npm -v nvm currentnode -v拿到当前运行时版本npm -v确认包管理器版本nvm current只在装了 nvm 时可用。注意一个细节Vite 5 构建脚本内部依赖 Node 的 ESM loaderNode 18.0 以下会出现ERR_UNSUPPORTED_NODE_MODULES之类的问题装到 18.17 才稳。如果你本机装的是 16.x我建议直接nvm install 20切过去别在旧版本上做兼容层适配纯属给自己加负担。2.2 依赖版本与 package.json 参考下面这份是一个能直接跑的三方依赖清单cesium放在dependenciesvite、typescript、vue-tsc放在devDependencies{ name: cesium-vue3-ts-skeleton, private: true, scripts: { dev: vite, build: vue-tsc -b vite build, preview: vite preview }, dependencies: { cesium: ^1.118.0, vue: ^3.4.0 }, devDependencies: { vitejs/plugin-vue: ^5.0.0, typescript: ^5.4.0, vite: ^5.2.0, vue-tsc: ^2.0.0 } }版本号只是示例npm install 时^会拉到对应次版本的最新补丁不影响整体兼容。这里的 scripts 有讲究build先跑vue-tsc -b做类型检查再跑vite build避免类型错误混进产物。很多模板只写vite build结果 CI 里类型错误根本发现不了等部署上去才炸。2.3 为什么不用 CDN、也不用 Vue2 组合先说 CDN 方案为什么不合适。直接用script srchttps://cesium.com/downloads/cesium/js/release/.../script这类标签引入初始化代码大概率写在onload里一旦加载顺序错一点控制台就是Cesium is not defined。更麻烦的是Cesium 的 worker 与Assets目录默认按脚本相对路径查找CDN 场景下这些路径要么存在跨域问题要么被构建工具的base路径撞碎很难做到开箱即用。再说 Vue2。Vue2 组合式 API 虽然也能跑但 Vue2 的响应式代理对Cesium.Viewer这种包含大量非普通对象、内部状态不断变更的实例很不友好连shallowRef都要额外装vue/composition-api补丁。与其在旧栈上缝缝补补不如直接用 Vue3 Vite 原生生态。这套组合是当前三维可视化最省心的底盘组件卸载和响应式处理都是现成的。3. 工程骨架搭建初始化、安装依赖、搬运 Cesium 静态资源搭建阶段的目标只有一个让 Cesium 在 Vite 开发服务器里能以完整形态跑起来。Cesium 不像普通 npm 包做成 bundle 就结束它有Workers里的 web worker、Widgets里的 UI 样式、Assets里的图片和 glTF这些必须作为静态资源被服务器直接访问。所以整个安装流程可以拆成三步建工程、装依赖、把 Cesium 的Build/Cesium目录搬到public下。3.1 从模板仓库直接拉骨架如果团队里已经有维护过一段时间的 Cesium 工程用 degit 拉取骨架效率最高。我一般会把skeleton/目录独立出来里面只放 config、scripts、src/env.d.ts和 package.json不携带业务代码新项目 clone 下来装完依赖就能开发degit 你的模板地址 my-cesium-app cd my-cesium-app npm install npm run dev尖括号里的模板地址换成你自己的仓库路径即可。degit 的好处是只拉文件不保留 git 历史导出后直接git init重新管理版本避免把模板的提交记录带进新项目。没有 degit 就先用git clone代替效果相同只是会多一份历史。3.2 手工初始化 ViteVue3TS没有现成模板时手工初始化也不复杂。Vite 官方脚手架支持--template参数一步到位npm create vitelatest my-cesium-app -- --template vue-ts cd my-cesium-app npm install npm install cesium--template vue-ts会生成带 TypeScript 和 Vue3 的基础结构默认内容很少正好适合我们接着铺 Cesium。npm install cesium会把 Cesium 主包放进node_modules。这里提醒一句不要同时安装types/cesiumCesium 从 1.61 起已经自带完整的.d.ts类型声明第三方类型包只会造成声明冲突属于典型的画蛇添足。3.3 设置 CESIUM_BASE_URL 与静态资源搬运Cesium 在运行时查找静态资源的逻辑是这样的优先读window.CESIUM_BASE_URL读不到就根据当前脚本地址去推导。Vite 打包后所有代码都打进 bundle脚本地址被改得面目全非推导必然出错所以必须手动指定CESIUM_BASE_URL并确保该路径下真的有对应资源。先抄近路把 Cesium 的构建产物整个放到public/cesiumcp -r node_modules/cesium/Build/Cesium public/cesiumWindows PowerShell 下换成Copy-Item -Path node_modules\cesium\Build\Cesium -Destination public\cesium -Recurse -ForceBuild/Cesium目录里包含Assets/、Workers/、Widgets/、ThirdParty/以及index.js、index.d.ts等核心文件。其中的Workers必须完整保留缺失任何一个 worker 文件后续地形加载和模型渲染都会静默失败只会在浏览器控制台留下一串 404。这也是很多集成项目跑起来“看起来没问题、一拖拽就报错”的根源。接着在入口文件里设置路径。我习惯在src/env.d.ts中补充Window接口再在main.ts顶部写入window.CESIUM_BASE_URL ${import.meta.env.BASE_URL}cesium这段代码必须位于任何from cesium的 import 语句之前。import.meta.env.BASE_URL是 Vite 注入的部署基本路径默认是/如果你之后把应用部署到二级目录只需改 Vite 的base配置这里会跟着变。改成硬编码/cesium也行但后续部署迁移时容易漏改。提示拷贝完成后先确认public/cesium/Workers/createTaskProcessorWorker.js这个文件真实存在再启动 dev server。public 目录下文件变更一般不需要重启但浏览器缓存偶尔会卡一下旧文件遇到诡异现象就强制刷新一次。3.4 写一个最小的 Viewer 跑起来基础配置就绪后用最小可运行组件验证整条链路。把src/App.vue改成下面这样script setup langts import { onBeforeUnmount, onMounted, ref } from vue import * as Cesium from cesium import cesium/Build/Cesium/Widgets/widgets.css const container refHTMLDivElement | null(null) let viewer: Cesium.Viewer | null null onMounted(() { if (!container.value) return viewer new Cesium.Viewer(container.value, { animation: false, timeline: false, baseLayerPicker: false, geocoder: false, homeButton: false, sceneModePicker: false, navigationHelpButton: false, infoBox: false, selectionIndicator: false, shouldAnimate: true }) viewer.scene.globe.enableLighting true }) onBeforeUnmount(() { if (viewer) { viewer.destroy() viewer null } }) /script template div refcontainer stylewidth: 100vw; height: 100vh/div /templatewidgets.css是 Cesium 控件样式的必需资源漏了它会出现地图能显示但工具栏样式全部错乱的情况。Viewer 构造参数里我把非必要控件全关了animation关掉动画帧率控件timeline关掉底部时间轴geocoder关掉搜索框baseLayerPicker关掉底图切换器这里提一下infoBox和selectionIndicator如果留着点击实体时会弹出原生信息面板在大屏项目里很影响观感。shouldAnimate: true让时间轴自动推进涉及粒子、模型动画时有明显差别。container用模板 ref 绑定真实 DOM而不是传字符串 id。开发环境遇到container is undefined大多因为组件执行时 DOM 还没挂载放在onMounted里执行就一定存在。容器必须显式设置宽高Cesium 初始化时会读取容器尺寸如果父级高度为 0球体渲染出来就是一片空白。4. TypeScript 集成把 Cesium 类型吃得透、不靠 ts-ignore很多模板为了让 TypeScript 不报错在src里写一个declare module cesium把整个包声明成 any。这样做确实能过关但开发体验直接回到 JS 时代Cesium 的相机参数、几何结构、材质配置全部失去补全提示写错参数只能在运行时发现。集成思路应该是“充分利用 Cesium 自带类型再补齐 Vue 生态的类型缺口”。4.1 认清 Cesium 自带类型与包内声明指向Cesium npm 包的package.json已经声明了主入口和类型指向安装完成后node_modules/cesium下的index.d.ts就是完整类型来源。用以下方式导入即可获得完整推断import * as Cesium from cesium不要再加一层declare module cesium去覆盖它。原因很简单一旦你这么写TypeScript 会优先使用你声明的模块结构Cesium 内部几百个类之间相互引用的类型全被剪断编辑器甚至会提示Cesium.Viewer类型其实就是any。正确做法是让node_modules里的声明文件成为唯一来源项目里只补充.vue模块声明和环境变量声明。4.2 env.d.ts 与 .vue 模块声明Vite 生成的src/vite-env.d.ts默认只有一行/// reference typesvite/client /。我会把它扩展成下面这样模板里直接复制即可/// reference typesvite/client / interface ImportMetaEnv { readonly VITE_APP_TITLE: string } interface ImportMeta { readonly env: ImportMetaEnv } interface Window { CESIUM_BASE_URL: string } declare module *.vue { import type { DefineComponent } from vue const component: DefineComponent{}, {}, any export default component }Window接口扩展是给第 3 章的window.CESIUM_BASE_URL提供类型*.vue模块声明解决单文件组件在 TS 环境下的默认导出识别问题。加ImportMetaEnv的好处是以后你用import.meta.env.VITE_APP_TITLE这类变量时编辑器能直接给出字符串补全而不是让你猜环境变量名。4.3 封装 useCesium 组合式用 shallowRef 避开响应式地雷工程里几个页面都要用到 viewer我习惯把它封装成一个组合式函数而不是在每个组件里复制粘贴初始化逻辑。核心注意点是永远不要ref(viewer)一个 Cesium 实例这会让 Vue 尝试对包含大量方法、事件、内部循环引用的对象做深度响应式代理轻则性能劣化重则循环引用直接栈溢出。正确的做法是用shallowRefimport { onBeforeUnmount, onMounted, ref, shallowRef, type Ref } from vue import * as Cesium from cesium export function useCesium(containerRef: RefHTMLElement | null) { const viewer shallowRefCesium.Viewer | null(null) const ready ref(false) onMounted(() { if (!containerRef.value) return viewer.value new Cesium.Viewer(containerRef.value, { animation: false, timeline: false, baseLayerPicker: false }) ready.value true }) onBeforeUnmount(() { viewer.value?.destroy() viewer.value null ready.value false }) return { viewer, ready } }shallowRef只代理最外层引用Cesium 实例内部状态变化不会触发 Vue 的响应式追踪。ready用普通ref包一层是为了给模板提供v-if条件告诉页面“viewer 已经准备好”避免子组件在 viewer 未初始化时就调 API。调用时先const { viewer, ready } useCesium(containerRef)再到ready.value为 true 后写业务逻辑这一套在大型项目里能省掉大量if (viewer.value)空判断。5. 常见问题排查五类典型翻车现场的处理姿势排错的核心不是看错误信息多炫而是抓住三个“有没有”静态资源是否物理存在、CESIUM_BASE_URL是否正确、Worker/Image 请求是否返回 200。我把自己踩过的五类场景按“现象 → 原因 → 解决”写下来前两类几乎每个从 CDN 迁移到 Vite 的人都会遇到。5.1 白屏与静态资源 404现象页面加载后一片空白控制台出现多条Failed to load resource: the server responded with a status of 404路径指向/cesium/Workers/xxx.js。有的还会连带报Could not load imagery但地球区域完全没绘制。原因Build/Cesium目录没有完整复制到public/cesium或者CESIUM_BASE_URL设置的值和资源实际路径不一致。这两种情况在浏览器 NetWork 面板里都会显示红色 404。解决先确认路径再确认文件物理存在。执行ls public/cesium/Workers/createTaskProcessorWorker.js ls public/cesium/Assets如果文件不存在重新执行搬运命令。若文件在但请求仍 404检查window.CESIUM_BASE_URL的运行时值。我习惯在main.ts里设置后加一行console.log(window.CESIUM_BASE_URL)打印确认这个方法简单粗暴却非常有效。注意 Vite 的public目录资源访问时是不带public前缀的所以public/cesium对应 url 就是/cesium。5.2 默认底图报 403 与全球影像加载失败现象地球能转但表面是灰色瓦片NetWork 里针对遥感影像服务的请求返回 403或者控制台出现An error occurred while processing this request。原因Viewer 默认底图走 Cesium Ion 服务而项目没有配置Ion.defaultAccessToken。Ion 对匿名请求限制很严格超过额度或资产被标记为私有都会返回 403。解决去官网注册账号申请一个默认 token在初始化前写入Cesium.Ion.defaultAccessToken 你的 token如果项目主要加载本地数据不想依赖在线服务直接换成本地单图源最省心viewer.imageryProvider new Cesium.SingleTileImageryProvider({ url: ${import.meta.env.BASE_URL}data/base.jpg })SingleTileImageryProvider会把一张大图拉伸成覆盖全球的底图适合做园区或小范围场景的冒烟验证绕开 token 限制。5.3 组件切换后内存只涨不降现象在 Vue 路由里反复进入和退出三维页面浏览器内存稳步上涨多次切回后页面明显卡顿。原因Vue 组件卸载只移除了 DOM 节点Cesium.Viewer内部创建的 WebGL 上下文、worker 线程、事件监听还挂在全局。只要viewer变量没有销毁GPU 缓冲和网络请求就一直在后台运行。单纯把组件v-if置空是挡不住泄漏的。解决完整的销毁代码应该写进onBeforeUnmountonBeforeUnmount(() { viewer?.scene.postRenderEvent.removeAll() viewer?.destroy() viewer null container.value?.replaceChildren() })postRenderEvent.removeAll()先清掉自定义渲染回调防止销毁过程中回调又触发渲染destroy()释放 WebGL 和 workerreplaceChildren()清空容器内残留的 canvas。如果页面用了EntityCollection可以再把viewer.entities.removeAll()先执行一遍。这一套下来内存曲线基本是稳定的。5.4 pnpm 符号链接导致 Worker 构造失败现象用 pnpm 安装依赖后启动页面直接抛Failed to construct Worker或者控制台报worker relative path error但同样的代码用 npm 安装就没有问题。原因pnpm 默认用符号链接组织node_modulesCesium 的Workers内部通过相对路径计算 worker 文件地址符号链接打乱了真实目录层级worker 文件找不到了。解决本项目用的搬运方案其实已经把资源拷到public/cesium符号链接问题就被绕开了。如果仍遇到问题可以在 package.json 里加一个postinstall钩子让每次依赖安装后自动同步资源postinstall: node scripts/copy-cesium.mjs对应的scripts/copy-cesium.mjsimport { cpSync } from node:fs import { fileURLToPath } from node:url import { dirname, resolve } from node:path const root resolve(dirname(fileURLToPath(import.meta.url)), ..) cpSync( resolve(root, node_modules/cesium/Build/Cesium), resolve(root, public/cesium), { recursive: true } ) console.log(cesium assets copied to public/cesium)cpSync的recursive: true会递归复制整个目录.mjs后缀确保脚本以 ESM 方式执行不受项目 package.json 里type字段影响。加了 postinstall 后团队任何人安装依赖都不会漏掉资源搬运这一步。5.5 打包阶段 JavaScript heap out of memory现象npm run build执行到一半崩溃控制台报FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory。原因Cesium 打包体量不小Vite 在 Rollup 构建时默认 Node 堆内存上限较低大项目在压缩阶段就会撞到上限。解决临时提高 Node 堆内存再构建。Linux/macOS 执行NODE_OPTIONS--max-old-space-size4096 npm run buildWindows PowerShell 下先设置环境变量再执行set NODE_OPTIONS--max-old-space-size4096 npm run build也可以直接把build脚本改成build: NODE_OPTIONS--max-old-space-size4096 vue-tsc -b vite build注意 Windows 上这种写法需要额外安装cross-env否则NODE_OPTIONS不会被识别。如果你只在 Linux CI 上打包直接改脚本最省心。6. 装完后如何验证六项清单与一趟最小冒烟测试环境装完、排错也过了一遍以后真正判断这套模板是否“开箱即用”我习惯按下面的顺序做一次最小冒烟。整个过程控制在十分钟以内从 dev server 启动到页面显示地球不写任何业务数据代码。6.1 六项验证清单检查项预期结果快速定位方法控制台无 404不出现/cesium/Workers或/cesium/Assets相关报错打开 NetWork 面板筛选 Status 4xxCESIUM_BASE_URL 生效window.CESIUM_BASE_URL输出/cesium控制台直接执行window.CESIUM_BASE_URLWorker 正常创建请求/cesium/Workers/createTaskProcessorWorker.js返回 200NetWork 面板搜索 workerWidgets 样式正常工具栏和比例尺样式不松散对比widgets.css是否已 import销毁后内存回稳反复切换路由后内存不单调上涨Performance 面板记录 Heap Size构建可通过npm run build完成且无类型错误看构建日志 exit code 是否为 06.2 用本地影像做冒烟测试绕开在线底图和 token 的干扰把最小验证落在本地资源上const localImagery new Cesium.SingleTileImageryProvider({ url: ${import.meta.env.BASE_URL}data/base.jpg }) viewer.imageryProvider localImagery在public/data目录放一张 jpg 图片刷新页面后底部出现该图片纹理说明整条 Cesium 渲染管线已经贯通。这一步能同时验证静态资源路径、WebGL 初始化、图层替换逻辑比直接看默认全球底图更容易定位问题。6.3 顺手验证按需渲染打开 Performance 面板你会发现即使画面静止Cesium 默认也在持续渲染。我一般在验证阶段顺手测一下按需渲染viewer.scene.requestRenderMode true viewer.scene.globe.maximumScreenSpaceError 2 viewer.scene.requestRender()requestRenderMode开启后只有场景变化或调用requestRender()时才执行渲染帧屏幕静止时 CPU 占用会立刻降下来。maximumScreenSpaceError控制瓦片细分的屏幕误差阈值调高能减少加载瓦片数调低更清晰这个参数后续可以根据部署机器性能再调。从那以后我再搭这类集成工程都会先复制Build/Cesium到public/cesium再打开 NetWork 面板确认 Worker 请求是 200才去写业务组件。这套验证顺序没让我再翻过车。希望帮到你。本文还有配套的精品资源点击获取