Appearance
gis-cesium 库开发规范
适用于
packages/gis-cesium— 项目核心 SDK。 这是整个技术栈中复杂度最高、约束最严的包。
定位
@nexa/gis-cesium 是 CesiumJS 的扩展 SDK。它保留 Cesium 公共 API 的可组合性,并对跨产品复用、资源生命周期、性能策略和版本兼容等复杂能力进行工程化封装。
- GIS 引擎:
CesiumViewer生命周期管理、Entity CRUD、时钟控制 - 特效系统:动态可视化效果(探测/爆炸/轨迹等)
- 插件体系:
viewer.extend(mixin)模式的能力扩展 - 材质库:自定义 GLSL 材质(闪烁/脉冲/流动线等)
- 图层管理:GeoJSON/底图等图层 CRUD
- 工具集:坐标转换、卫星轨道计算、时间工具等
扩展边界
- 允许公共 API 使用 Cesium 的稳定公共类型,不为
Viewer、Entity、Cartesian3、JulianDate创建无语义包装 - 必须封装事件监听、DOM、Entity、Primitive、PostProcessStage、ParticleSystem 等资源的所有权和释放流程
- 必须隔离 Cesium 私有 API;私有 API 只能进入专用兼容适配器,禁止散落在 Effect、Plugin 和产品代码中
- 禁止仅为“业务不能调用 Cesium”而重复包装一对一原生方法
CesiumViewer提供托管式启动体验;接受 Viewer 的扩展能力应兼容原生Cesium.Viewer
资源所有权
创建资源的模块就是资源 owner。owner 必须保存资源引用,并提供幂等释放行为:
| 资源 | 最低释放要求 |
|---|---|
| Entity / Primitive / DataSource | 只移除自身创建的实例 |
| ScreenSpaceEventHandler | 调用 destroy(),不得遗留输入监听 |
| clock / scene 事件 | 保存 remove callback 并调用 |
| DOM 节点 | 从挂载容器移除并清理引用 |
| PostProcessStage / ParticleSystem | 从所属 collection 移除,必要时销毁 |
调用方传入的 Viewer、集合、Entity 和外部资源默认归调用方所有,SDK 禁止越权销毁。
目录结构
packages/gis-cesium/
├── src/lib/ # 【库代码】— 对外发布
│ ├── index.ts # 总入口:export * from './NexaCesium'
│ ├── NexaCesium.ts # 版本号 VERSION + re-export 所有模块
│ └── core/
│ ├── AirViewer.ts # CesiumViewer 主类
│ ├── types.ts # CesiumViewerOptions / ClockOptions
│ ├── BaseMapManager.ts # 底图管理器
│ ├── TerrainManager.ts # 地形管理器
│ ├── effects/ # 动态特效
│ │ ├── index.ts / types.ts
│ │ └── XxxEffect.ts
│ ├── plugins/ # Viewer 扩展
│ │ ├── index.ts / types.ts
│ │ └── viewerXxxMixin.ts
│ ├── materials/ # 自定义材质
│ │ ├── index.ts / types.ts / Material.ts
│ │ └── Shaders/
│ ├── entities/ # 实体系统
│ ├── entities-connection/ # 实体连线特效
│ ├── layers/ # 图层管理
│ ├── tools/ # 工具函数
│ ├── utils/ # 底层工具
│ ├── mouse-events/ # 鼠标交互
│ ├── draw/ # 绘制工具
│ └── popup/ # 弹窗
├── src/examples/ # 示例中心(auto-discover)
├── src/components/ / views/ / routers/ # 示例 App UI
├── src/composables/ # 示例 App composable
├── src/assets/ # 静态资源(3D模型/地形/底图)
├── scripts/ # 辅助脚本
└── docs/ # VitePress 文档站模块开发流水线
每个新功能模块必须按以下步骤执行:
Phase 1: 模块识别
根据功能特征确定模块类型(详见 AGENTS.md 模块模式约束表):
| 特征 | → 模块类型 | → 目录 |
|---|---|---|
| 有 on/off/destroy 生命周期 | effect | effects/ |
| 通过 viewer.extend() 注入 | plugin | plugins/ |
| 涉及 Cesium MaterialProperty/GLSL | material | materials/ |
| Entity 创建工厂 | entity | entities/ |
| 独立纯函数,不依赖 viewer | tool | tools/ |
| CRUD 管理图层 | layer | layers/ |
| 底层渲染工具(FBO 等) | util | utils/ |
Phase 2: 类型定义先行
1. 在目标模块的 types.ts 中定义 Options 接口
2. 所有公共 interface 必须有 JSDoc
3. 可选参数提供默认值说明Phase 3: 实现
按模块类型模板编写实现文件(详见 .claude/skills/create-feature.md)。
共同要求:
- 所有导出函数/类必须有完整 JSDoc(含
@example) - 类使用 PascalCase,函数使用 camelCase
- 私有成员以
_前缀 - Cesium 类型通过
import type * as Cesium from 'cesium'导入
Phase 4: 注册导出
1. 模块 index.ts 添加导出
2. NexaCesium.ts 确认 re-export(通常模块 index 已覆盖)Phase 5: 示例(可视化功能必须)
在 src/examples/templates/<Name>/ 创建:
index.ts— 示例定义(key 必须全局唯一)index.vue— Vue 容器index.code.ts— 可运行代码(使用全局 NexaGIS / Cesium)
Phase 6: 验证
bash
pnpm lint
pnpm typecheck
pnpm --filter @nexa/gis-cesium run build:lib
pnpm --filter @nexa/gis-cesium testEffect 开发规范
类结构
typescript
import type { XxxOptions } from './types'
import type * as Cesium from 'cesium'
export class XxxEffect {
private _enabled = false
private _source: Cesium.Entity
// ...
constructor(options: XxxOptions) {
this._source = options.source
// 参数校验(必填参数在构造函数中检查)
}
/** 启用效果,将效果实体添加到 collection */
on(collection: Cesium.EntityCollection): void {
if (this._enabled) return // 幂等
this._enabled = true
// 添加效果实体
}
/** 禁用效果,移除效果实体 */
off(collection: Cesium.EntityCollection): void {
if (!this._enabled) return
this._enabled = false
// 移除效果实体
}
/** 销毁效果,释放所有引用 */
destroy(): void {
this._enabled = false
// 清理引用、移除事件监听
}
}
export default XxxEffect要求
on/off必须幂等(重复调用不产生副作用)off只释放运行期资源,允许后续再次ondestroy释放全部内部资源;是否允许再次启用必须由公共契约明确说明- 已发布 Effect 暂不统一修改方法签名;签名迁移必须使用
@deprecated过渡,禁止批量破坏兼容性 - 构造函数中校验必填参数
Plugin 开发规范
函数签名
typescript
export function viewerXxxMixin(
viewer: Cesium.Viewer,
options?: XxxMixinOptions
): void {
// 通过 Object.defineProperties 挂载 API 到 viewer
Object.defineProperties(viewer, {
xxx: {
get: () => ({ /* 暴露的 API */ }),
configurable: true,
},
})
}要求
- 函数名以
viewer开头,以Mixin结尾 - API 通过
viewer.xxx访问 - 选项接口定义在
plugins/types.ts
Material 开发规范
类结构
typescript
import { CsmMaterial } from './Material'
import shaderSource from './Shaders/Xxx.glsl'
import type { XxxMaterialValue } from './types'
export class XxxMaterialProperty {
constructor(options: XxxMaterialPropertyOptions) {
// 注册材质到 Cesium
CsmMaterial.addMaterial('XxxType', {
fabric: {
type: 'XxxType',
uniforms: { /* ... */ },
source: shaderSource,
},
})
}
getType(): string { return 'XxxType' }
getValue(time: Cesium.JulianDate, result?: XxxMaterialValue): XxxMaterialValue { ... }
equals(other: Cesium.Property): boolean { ... }
}要求
- GLSL 着色器文件放在
Shaders/子目录 - 通过
CsmMaterial.addMaterial()注册 - 实现
getType/getValue/equals
公共 API 管理
NexaCesium.ts 规则
- 这是库的唯一入口 barrel 文件
- 每个核心模块至少在此有一个 re-export
- 版本号
VERSION在此定义 - 修改此文件视为破坏性变更(需 PR 审查)
导出规范
typescript
// 默认导出 + 命名导出(Effect 类)
export { default as XxxEffect } from './core/effects/XxxEffect'
// 纯命名导出(Plugin 函数、Tool 函数)
export { viewerXxxMixin } from './core/plugins/viewerXxxMixin'
// 纯命名导出(Material 类)
export { XxxMaterialProperty } from './core/materials/XxxMaterialProperty'禁止事项
- ❌ 在库代码中引入
src/App.vue、src/main.ts、src/views/等示例 App 文件 - ❌ 在库代码中引入
naive-ui等 UI 组件(库代码应保持 UI 无关) - ❌ 修改
NexaCesium.ts的导出而不更新文档和示例 - ❌ 在公共 API 中使用
any(packages/ 下no-explicit-any: error) - ❌ 删除或修改现有公共 API 的签名(视为 Breaking Change)