React+Vite+Cesium实现地球组件
·
此次项目改用了React,结合Vite构建项目,要集成Cesium实现一个三维的地球。本文将从环境搭建到项目实战,一步步教你如何实现这一功能。
本文代码已上传至Gitee仓库,可直接克隆使用
git clone https://gitee.com/z-beast/cesium-react-demo.git
一、基础环境准备
cesium官网:https://cesium.com/learn/cesiumjs-learn/
Cesium 部分功能需要 AccessToken 密钥支持,申请步骤如下:
1. 访问官网注册并登录 https://ion.cesium.com/signup
2. 登录成功后,在 AccessTokens 页面下,可复制token的值

二、项目集成Cesium
1. 创建 React+TypeScript+Vite 项目
在你想放置项目的文件夹下 打开终端,执行以下命令创建基础项目(项目名称可自定义)
# 使用 npm(推荐)
npm create vite@latest cesium-react-demo -- --template react-ts
进入项目目录
cd cesium-react-demo
# 安装依赖
npm install
启动开发服务器
npm run dev
访问 http://localhost:5173 即可看到项目运行。
2. 安装依赖
npm install cesium@^1.140.0
npm install vite-plugin-cesium@^1.2.23 -D
vite-plugin-cesium:这个插件会自动处理Cesium静态资源的路径问题
3. Vite核心配置
修改项目根目录下的 vite.config.ts 文件,配置 Cesium 插件,解决静态资源加载问题:
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import cesium from 'vite-plugin-cesium'
export default defineConfig({
plugins: [
react(),
cesium() // 集成 Cesium 插件,自动处理资源拷贝、按需加载
],
// 可选:配置路径别名(如需简化 Cesium 导入路径)
resolve: {
alias: {
'@': '/src',
'cesium-src': 'cesium/Build/Cesium' // 简化 Cesium 核心模块导入
}
}
})
配置说明:
- 插件顺序:cesium() 插件需在 react() 之后注册,确保组件能正常识别 Cesium 资源
- 静态资源处理:vite-plugin-cesium 会自动拷贝 Cesium 的 Build/Cesium 目录下的静态资源(如纹理、字体、Worker 脚本)到构建目录,无需手动复制
- 别名配置:通过 cesium-src 别名可简化导入路径(如 import 'cesium-src/Widgets/widgets.css'),避免写冗长的相对路径
- 打包兼容:插件已内置 Cesium 打包优化,解决 require 语法兼容、Worker 脚本打包等问题,无需额外配置 build.rollupOptions
4. 配置Token环境变量
在项目根目录创建 .env.development 文件,配置 Cesium Ion Token
VITE_CESIUM_ION_TOKEN="你的 Cesium Ion Token"
- 前缀
VITE_是 Vite 环境变量的固定前缀,确保在组件中通过import.meta.env访问 - 替换
你的 Cesium Ion Token为第一步申请的真实 Token,若未配置 Token,部分在线资源将无法加载
5. Cesium 组件核心封装
创建 src/components/Cesium.tsx组件,封装 Cesium 初始化、容器挂载、生命周期管理等核心逻辑,实现组件化复用:
import { useEffect, useRef, useState } from 'react';
import * as Cesium from 'cesium';
import { Viewer, Cartesian3, Color, Ion, Entity } from 'cesium';
// 导入 Cesium 样式文件(必须引入,否则控件样式异常)
import 'cesium/Build/Cesium/Widgets/widgets.css';
import './index.scss';
const {VITE_CESIUM_ION_TOKEN} = import.meta.env
// 使用你的Token令牌
Cesium.Ion.defaultAccessToken = VITE_CESIUM_ION_TOKEN
const CesiumEarth: React.FC<{}> = () => {
const center = [116.41, 39.52, 16000000] as [number, number, number];
const containerRef = useRef<HTMLDivElement>(null);
const viewerRef = useRef<Cesium.Viewer | null>(null);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
if (!containerRef.current) return;
try {
setIsLoading(true);
// 创建 Viewer
const viewer = new Cesium.Viewer(containerRef.current, {
// 控件配置 显示/隐藏
shouldAnimate: false,
animation: false, //动画
baseLayerPicker: true, //图层选择器
fullscreenButton: true, //全屏按钮
vrButton: false,
homeButton: true, //返回默认视角
infoBox: true,
sceneModePicker: true, //场景模式切换(2D/3D)
selectionIndicator: false,
timeline: false, //时间轴
navigationHelpButton: true, //帮助说明按钮
navigationInstructionsInitiallyVisible: false, //帮助说明是否默认打开
// 性能优化
targetFrameRate: 60,
useBrowserRecommendedResolution: false,
});
viewerRef.current = viewer;
// 视角定位到中国-北京区域
if (viewerRef.current) {
viewerRef.current.camera.flyTo({
destination: Cesium.Cartesian3.fromDegrees(center[0], center[1], center[2]),
duration: 2 // 动画时长(秒),默认3
});
}
setIsLoading(false);
} catch (err) {
console.error('Failed to initialize Cesium:', err);
setError(err instanceof Error ? err.message : 'Failed to initialize Cesium');
setIsLoading(false);
}
// 清理函数
return () => {
if (viewerRef.current) {
viewerRef.current.destroy();
viewerRef.current = null;
}
};
}, []); // 只在组件挂载时初始化一次
return (
<div className="cesium-earth">
<div ref={containerRef} className="cesium-viewer-container" />
{isLoading && (
<div className="loading-overlay">
<div className="loading-spinner" />
<p>加载地球中...</p>
</div>
)}
{error && (
<div className="error-overlay">
<p>错误: {error}</p>
<button onClick={() => window.location.reload()}>重试</button>
</div>
)}
</div>
)
};
export default CesiumEarth;
同目录下样式文件 index.scss
.cesium-earth {
width: 100%;
height: 100vh;
position: absolute;
overflow: hidden;
.error-overlay {
display: none;
}
}
.cesium-viewer-container {
width: 100%;
height: 100%;
/** 使用css隐掉控件工具*/
.cesium-viewer-geocoderContainer,
.cesium-widget-credits {
display: none !important;
}
}
.loading-overlay,
.error-overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
background: rgba(0, 0, 0, 0.8);
color: white;
z-index: 1000;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}
.loading-spinner {
width: 40px;
height: 40px;
border: 3px solid rgba(255, 255, 255, 0.3);
border-top-color: #fff;
border-radius: 50%;
animation: spin 1s linear infinite;
margin-bottom: 15px;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
.error-overlay button {
margin-top: 15px;
padding: 8px 20px;
background: #ff4444;
border: none;
color: white;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
}
.error-overlay button:hover {
background: #ff6666;
}
/* 响应式调整 */
@media (max-width: 768px) {
.loading-overlay p,
.error-overlay p {
font-size: 14px;
padding: 0 20px;
text-align: center;
}
}
封装亮点与避坑解析
- 生命周期管理:在 useEffect() 中初始化 Viewer,并在 return 中销毁实例,避免内存泄漏(Cesium 实例若不手动销毁,会残留大量 DOM 和事件监听)
- 样式引入:必须导入 cesium/Build/Cesium/Widgets/widgets.css样式文件,否则 Cesium 控件会样式错乱
- Token 动态配置:从环境变量读取 Token,避免硬编码,提高项目安全性和可配置性
- 控件自定义:通过 Viewer 配置项灵活控制控件显示,满足不同项目需求
- 视角初始化:通过 camera.flyTo 定位到目标区域,提升用户体验
6. 项目组件使用
在根组件中 App.tsx 中使用 Cesium.tsx
import './App.css'
import Cesium from './components/Cesium'
function App() {
return (
<Cesium />
)
}
export default App
随后即可看到全屏的 Cesium 地球场景,支持鼠标拖拽旋转、滚轮缩放、控件操作等功能。

更多推荐


所有评论(0)