Skip to content

核心架构

本文档描述了支撑所有 Leaflet 组件的基础架构模式。它涵盖了类系统、事件处理、核心工具函数以及构成地图、图层、控件和处理器基础的坐标抽象。

有关 LeafletMap 组件本身的信息,请参阅地图组件。有关事件传播和模式的详细信息,请参阅事件系统。有关辅助函数和 Class 基类,请参阅工具与类系统。有关坐标转换和投影,请参阅地理坐标与投影

基础类层次结构

Leaflet 的架构建立在少量基类之上,这些基类通过继承提供通用功能。Class 基类提供了面向对象编程的基础,而 Evented 扩展了它以添加几乎所有 Leaflet 对象都使用的事件功能。

图示:核心类层次结构与继承链

SVG
100%

类系统

Class 基类提供了 Leaflet 的继承机制和选项管理。所有 Leaflet 类都继承自 Class 或其子类之一。

关键机制

机制静态方法实例方法用途
选项setDefaultOptions(options)this.options将默认选项合并到原型上
初始化addInitHook(fn)callInitHooks()在构造后运行钩子
混入include(props)-向类原型添加方法
合并mergeOptions(options)-将额外选项合并到默认值中

初始化流程

当构造一个 Leaflet 对象时,会发生以下序列:

SVG
100%

选项继承

选项通过原型链继承。当调用 setOptions 时,它会创建一个新的选项对象,该对象继承自父级的选项:

SVG
100%

事件系统 (Evented)

Evented 扩展了 Class 以提供发布-订阅事件系统。大多数 Leaflet 类继承自 Evented 以实现事件驱动架构。

事件注册与触发

图示:事件生命周期

SVG
100%

事件处理器存储

事件处理器存储在 _events 对象中,按事件类型键控。每种事件类型映射到一个监听器对象数组:

// 内部结构
this._events = {
    'click': [
        {fn: handlerFn1, ctx: contextObj1, once: false},
        {fn: handlerFn2, ctx: undefined, once: true}
    ],
    'move': [
        {fn: moveHandler, ctx: undefined, once: false}
    ]
}

上下文优化

当使用 context === this 注册监听器时,上下文被设置为 undefined 以减少内存占用,因为默认行为无论如何都会使用 this 调用:

事件父级与传播

Leaflet 支持通过事件父级进行分层事件传播。当使用 propagate=true 调用 fire() 时,事件会通过注册父级对象冒泡:

SVG
100%

事件父级使用 Util.stamp() 生成唯一 ID 存储在 _eventParents 中:

核心工具函数

Util 命名空间提供了整个 Leaflet 使用的辅助函数。这些是纯函数,没有状态。

基本工具函数

函数签名用途
stamp(obj)Object → Number通过 _leaflet_id 属性分配并返回唯一 ID
setOptions(obj, options)Object, Object → Object将选项合并到 obj.options,返回合并后的选项
throttle(fn, time, context)Function, Number, Object → Function返回节流函数,每 time 毫秒最多执行一次
template(str, data)String, Object → String使用数据对象计算模板字符串 'Hello {name}'
formatNum(num, precision)Number, Number → Number将数字四舍五入到 precision 位小数(默认 6)
splitWords(str)String → String[]在空白字符处修剪并分割字符串
wrapNum(num, range, includeMax)Number, Number[], Boolean → Number通过取模将数字包装在范围内

图示:Util.stamp() 使用模式

SVG
100%

模板系统

template() 函数支持简单的字符串插值,用于瓦片 URL 生成和其他动态字符串:

SVG
100%

坐标系统与几何

Leaflet 在渲染管道的不同空间中使用不同的坐标类型。理解这些类型是使用该库的基础。

坐标类型层次结构

SVG
100%

坐标转换管道

LeafletMap 类提供了不同坐标空间之间的转换方法:

方法描述
latLngToLayerPoint(latlng)LatLngPoint地理 → 图层点(相对于像素原点)
layerPointToLatLng(point)PointLatLng图层点 → 地理
latLngToContainerPoint(latlng)LatLngPoint地理 → 容器点(相对于地图容器)
containerPointToLatLng(point)PointLatLng容器点 → 地理
project(latlng, zoom)LatLngPoint地理 → 缩放级别下的投影点
unproject(point, zoom)PointLatLng投影点 → 地理

图示:坐标空间转换

SVG
100%

点与边界操作

PointBounds 类提供了向量数学和几何操作:

SVG
100%

架构模式

初始化钩子模式

Leaflet 使用初始化钩子允许子类注入行为而无需覆盖构造函数。这实现了清晰的组合:

SVG
100%

使用示例:

// 在 LeafletMap 类定义中
LeafletMap.addInitHook('_initContainer', id);
LeafletMap.addInitHook('_initLayout');
LeafletMap.addInitHook('_initEvents');

// 替代函数形式
LeafletMap.addInitHook(function () {
    if (this.options.maxBounds) {
        this.setMaxBounds(this.options.maxBounds);
    }
});

选项合并模式

Leaflet 的选项系统使用原型继承,允许在类层次结构的每个级别设置默认值:

SVG
100%

事件父级模式

事件父级模式支持分层事件冒泡,通常用于图层组内的图层:

SVG
100%

与 LeafletMap 集成

基础类集成形成完整的地图系统。LeafletMap 类展示了这种集成:

SVG
100%

关键集成点:

  1. 类系统:LeafletMap 使用 setDefaultOptions() 定义默认地图选项,使用 addInitHook() 注册初始化步骤
  2. 事件系统:LeafletMap 扩展 Evented 以触发 'move''zoom''click' 等事件并传播图层事件
  3. 工具函数:LeafletMap 使用 Util.stamp() 生成图层 ID,Util.setOptions() 合并选项,Util.throttle() 优化性能
  4. 坐标:LeafletMap 使用 CRS 转换提供 LatLng 和 Point 空间之间的转换方法

测试模式

核心架构经过广泛测试以确保可靠性:

测试套件测试关键断言
ClassSpec.js类继承、选项合并、初始化钩子选项正确继承,钩子按顺序运行
EventsSpec.js事件注册、触发、传播、上下文监听器以正确上下文调用,传播正常工作
UtilSpec.js工具函数stamp() 唯一性,throttle() 计时,template() 插值
MapSpec.js地图初始化、视图管理、坐标转换地图状态正确,转换准确