依赖层次
本文档描述 Turf.js monorepo 中使用的四层依赖架构。它解释了 100 多个包如何组织成分层结构,从通用的基础模块到依赖外部库的复杂算法。这种层次结构防止循环依赖并支持独立包的模块化消费。
有关三个基础模块本身的信息,请参阅 基础模块。有关模块如何按功能分类的信息,请参阅 模块分类。
四层系统概述
Turf.js 将其依赖组织成四个不同的层级。每一层都构建在前一层的基础上,创建一个严格的层次结构,确保在 monorepo 中不会发生循环依赖。
层级 0:外部依赖
外部依赖提供 Turf.js 本身未实现的专业算法和数据结构。这些库因其稳定性和性能特征而被精心选择。
| 库 | 目的 | 使用方 |
|---|---|---|
@types/geojson | GeoJSON 的 TypeScript 类型定义 | 所有 TypeScript 模块 |
jsts | 用于计算几何的 Java Topology Suite 移植 | @turf/buffer, @turf/union, @turf/intersect |
polyclip-ts | Martinez-Rueda 多边形裁剪算法 | @turf/mask, @turf/difference |
rbush | R-tree 空间索引 | @turf/unkink-polygon, @turf/clusters-kmeans |
skmeans | K-means 聚类实现 | @turf/clusters-kmeans |
sweepline-intersections | Bentley-Ottmann 线相交 | @turf/line-intersect |
concaveman | 凹包算法 | @turf/concave |
d3-geo | 地图投影 | @turf/projection |
earcut | 多边形三角剖分 | @turf/tesselate |
层级 1:基础模块
三个基础模块构成了整个依赖层次结构的基础。其他所有 Turf.js 模块都至少依赖这些包中的一个。
@turf/helpers
@turf/helpers 包是最基本的依赖,每个 Turf.js 模块都需要它。它提供:
- GeoJSON 工厂函数:
point()、lineString()、polygon()、featureCollection() - 单位转换工具:
radiansToLength()、lengthToRadians()、lengthToDegrees() - 常量:用于单位转换的
earthRadius、factors - 选项对象的类型定义
来自 packages/turf-rhumb-destination/package.json77 的依赖声明示例:
"@turf/helpers": "workspace:*"@turf/meta
@turf/meta 包提供大约 50+ 个模块使用的迭代工具。它能够高效地遍历 GeoJSON 结构:
coordEach():遍历所有坐标coordReduce():对坐标进行规约geomEach():遍历几何体flattenEach():遍历展平的特征segmentEach():遍历线段
来自 packages/turf-flatten/package.json72-73 的使用示例:
"@turf/helpers": "workspace:*",
"@turf/meta": "workspace:*"@turf/invariant
@turf/invariant 包提供输入验证和类型提取,大约 70+ 个模块使用它:
getCoord():从各种输入类型提取坐标数组getGeom():从特征或几何体提取几何体- 类型守卫:
getType()、geojsonType() - 集合工具:
collectionOf()、featureOf()
来自 packages/turf-rhumb-bearing/package.json72-73 的示例:
"@turf/helpers": "workspace:*",
"@turf/invariant": "workspace:*"层级 2:原始操作
层级 2 模块实现仅依赖基础层和外部库的基础几何操作。它们不依赖其他 Turf.js 操作。
层级 2 模块的特征
层级 2 模块具有共同特征:
- 最小依赖:仅依赖基础模块
- 单一职责:每个模块执行一个定义明确的计算
- 无 Turf.js 内部依赖:不导入其他 Turf.js 操作模块
- 高可重用性:被层级 3 和层级 4 模块用作构建块
来自 packages/turf-rhumb-distance/package.json74-79 的示例:
"dependencies": {
"@turf/helpers": "workspace:*",
"@turf/invariant": "workspace:*",
"@types/geojson": "^7946.0.10",
"tslib": "^2.8.1"
}此包仅依赖 @turf/helpers 和 @turf/invariant,使其成为真正的原始操作。
层级 3:复合操作
层级 3 模块组合来自层级 2 的原始操作以实现更复杂的功能。它们从较低级别的构建块构建更高级的功能。
示例:@turf/line-slice
@turf/line-slice 模块展示了层级 3 的组合。它依赖 @turf/nearest-point-on-line,其本身也是一个层级 3 模块:
来自 packages/turf-line-slice/package.json68-73:
"dependencies": {
"@turf/helpers": "workspace:*",
"@turf/invariant": "workspace:*",
"@turf/nearest-point-on-line": "workspace:*",
"@types/geojson": "^7946.0.10"
}这表明模块既依赖基础模块,又依赖另一个复合操作。
示例:@turf/line-chunk
@turf/line-chunk 模块展示了多个层级 3 依赖:
来自 packages/turf-line-chunk/package.json73-78:
"dependencies": {
"@turf/helpers": "workspace:*",
"@turf/length": "workspace:*",
"@turf/line-slice-along": "workspace:*",
"@turf/meta": "workspace:*",
"@types/geojson": "^7946.0.10"
}它依赖 @turf/length(层级 2)和 @turf/line-slice-along(层级 3),展示了复合操作如何相互构建。
层级 4:复杂算法
层级 4 模块实现复杂的算法,通常需要外部库和跨越层级 1-3 的多个 Turf.js 依赖。
示例:@turf/unkink-polygon
此模块展示了层级 4 依赖的复杂性:
来自 packages/turf-unkink-polygon/package.json68-76:
"dependencies": {
"@turf/area": "workspace:*",
"@turf/boolean-point-in-polygon": "workspace:*",
"@turf/helpers": "workspace:*",
"@turf/meta": "workspace:*",
"@types/geojson": "^7946.0.10",
"rbush": "^3.0.1",
"tslib": "^2.8.1"
}此模块需要:
- 基础模块:
@turf/helpers、@turf/meta - 层级 2 原语:
@turf/area - 层级 3 操作:
@turf/boolean-point-in-polygon - 外部库:用于空间索引的
rbush
示例:@turf/mask
来自 packages/turf-mask/package.json66-72:
"dependencies": {
"@turf/clone": "workspace:*",
"@turf/helpers": "workspace:*",
"@types/geojson": "^7946.0.10",
"polyclip-ts": "^0.16.8",
"tslib": "^2.8.1"
}此模块使用 polyclip-ts 库进行 Martinez-Rueda 多边形裁剪算法,展示了外部计算几何库的集成。
示例:@turf/line-intersect
来自 packages/turf-line-intersect/package.json71-76:
"dependencies": {
"@turf/helpers": "workspace:*",
"@types/geojson": "^7946.0.10",
"sweepline-intersections": "^1.5.0",
"tslib": "^2.8.1"
}使用 sweepline-intersections 库实现 Bentley-Ottmann 算法以进行高效的线段相交检测。
依赖链
复杂操作通常通过多个层级创建长依赖链。理解这些链有助于识别任何给定模块的传递依赖。
来自 packages/turf-point-to-line-distance/package.json69-81:
"dependencies": {
"@turf/bearing": "workspace:*",
"@turf/distance": "workspace:*",
"@turf/helpers": "workspace:*",
"@turf/invariant": "workspace:*",
"@turf/meta": "workspace:*",
"@turf/nearest-point-on-line": "workspace:*",
"@turf/projection": "workspace:*",
"@turf/rhumb-bearing": "workspace:*",
"@turf/rhumb-distance": "workspace:*",
"@types/geojson": "^7946.0.10",
"tslib": "^2.8.1"
}这展示了层级 4 模块如何聚合来自较低层级的多个依赖,每个依赖都带来其自己的传递依赖。
其他依赖模式
跨层级布尔操作
布尔操作通常依赖多个 Turf.js 模块来实现其功能:
来自 packages/turf-boolean-parallel/package.json68-75:
"dependencies": {
"@turf/clean-coords": "workspace:*",
"@turf/helpers": "workspace:*",
"@turf/line-segment": "workspace:*",
"@turf/rhumb-bearing": "workspace:*",
"@types/geojson": "^7946.0.10",
"tslib": "^2.8.1"
}@turf/boolean-parallel 模块依赖:
@turf/clean-coords(层级 3):移除重复坐标@turf/line-segment(层级 3):将线分割为段@turf/rhumb-bearing(层级 2):计算方位角
工具模块组合
某些模块组合多个工具以提供增强的功能:
来自 packages/turf-rewind/package.json72-80:
"dependencies": {
"@turf/boolean-clockwise": "workspace:*",
"@turf/clone": "workspace:*",
"@turf/helpers": "workspace:*",
"@turf/invariant": "workspace:*",
"@turf/meta": "workspace:*",
"@types/geojson": "^7946.0.10",
"tslib": "^2.8.1"
}@turf/rewind 模块使用 @turf/boolean-clockwise 在应用 rewind 操作之前确定多边形方向。
几何分析链
复杂的几何分析模块通常具有广泛的依赖链:
来自 packages/turf-polygon-tangents/package.json71-80:
"dependencies": {
"@turf/bbox": "workspace:*",
"@turf/boolean-within": "workspace:*",
"@turf/explode": "workspace:*",
"@turf/helpers": "workspace:*",
"@turf/invariant": "workspace:*",
"@turf/nearest-point": "workspace:*",
"@types/geojson": "^7946.0.10",
"tslib": "^2.8.1"
}Workspace 依赖
所有 Turf.js 包对内部依赖使用 "workspace:*" 协议,由 pnpm workspaces 管理。这确保 monorepo 中的所有包在开发期间正确引用彼此,并在发布时解析为适当的版本。
来自 packages/turf-sector/package.json65-72:
"dependencies": {
"@turf/circle": "workspace:*",
"@turf/helpers": "workspace:*",
"@turf/invariant": "workspace:*",
"@turf/line-arc": "workspace:*",
"@turf/meta": "workspace:*",
"@types/geojson": "^7946.0.10",
"tslib": "^2.8.1"
}workspace:* 协议在发布过程中由 Lerna 转换为具体的版本号,如 lerna.json1-10 中所配置。
主聚合包
@turf/turf 包作为主分发包,依赖所有 100+ 个独立包:
来自 packages/turf/package.json94-210:
"dependencies": {
"@turf/along": "workspace:*",
"@turf/angle": "workspace:*",
"@turf/area": "workspace:*",
// ... 100+ 更多依赖
"@turf/voronoi": "workspace:*",
"@types/geojson": "^7946.0.10",
"tslib": "^2.8.1"
}此聚合包允许用户通过单个依赖导入整个 Turf.js 库,同时保持模块化结构,支持 tree-shaking 和选择性导入。
防止循环依赖
层级系统通过严格规则防止循环依赖:
- 层级 1 模块不能依赖任何其他 Turf.js 模块
- 层级 2 模块只能依赖层级 1
- 层级 3 模块可以依赖层级 1 和 2
- 层级 4 模块可以依赖层级 1、2 和 3
- 任何模块如果会创建循环,则不能依赖同一层级的模块
这种层次结构通过以下方式强制执行:
- Pull Request 评估期间的代码审查
- 防止本地循环导入的 monorepo 结构
- 按依赖顺序编译的构建系统
结果是一个有向无环图(DAG)的依赖,确保:
- 可预测的构建顺序
- 无循环引用运行时错误
- 清晰的模块边界
- 消费应用程序中的高效 tree-shaking