Skip to content

Commit 0a3aa4d

Browse files
committed
fix(layers): unpin mapAfterFrameChange listener leak on destroy (stage-2 2.4)
1 parent 7861576 commit 0a3aa4d

3 files changed

Lines changed: 42 additions & 4 deletions

File tree

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
---
2+
'@antv/l7-layers': minor
3+
---
4+
5+
fix(layers): unpin mapAfterFrameChange listener leak on destroy
6+
7+
P4 阶段 2 第四刀(2.4,strictly-better:修复真实事件泄漏,零接口变化)。
8+
9+
`BaseLayer.init``enableMultiPassRenderer + passes` 下以 **inline arrow**
10+
注册 `mapService.on('mapAfterFrameChange', () => this.renderLayers())`
11+
`destroy()` 无对应 `off`——匿名箭头引用不可复现,即便加 off 也无法命中,
12+
监听器随图层销毁后仍挂在 `mapService` 上泄漏(图层重复创建/销毁场景累积)。
13+
14+
修复(与既有 `onSourceUpdate` 具名实例箭头同模式):
15+
16+
- inline arrow → `protected readonly onMapAfterFrameChange = (): void => { this.renderLayers(); }`
17+
稳定实例引用,on/off 配对解绑。
18+
- init 内 `on(..., this.onMapAfterFrameChange)`
19+
- destroy 内 **无条件** `this.mapService.off('mapAfterFrameChange', this.onMapAfterFrameChange)`
20+
(未注册时 off 为空操作,无害;条件化注册下统一 off 比 guard 更简洁安全)。
21+
22+
行为变化:仅修复泄漏,renderLayers 触发时机与频率不变。属 strictly-better,
23+
按 PLAN 阶段 2 归 minor。
24+
25+
验证:eslint 0 error、prettier 通过、layers father build 278 files(含
26+
declaration d.ts)、jest layers+maps 0 failed(77 suites,1 skipped)。

docs/refactoring/layers/PLAN.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ citybuilding/ geometry/ — 12 个具体图层 extends BaseLayer(各自只 ove
8888
- 2.1 `createPlugins()``init()` 内联 → 抽 `LayerPluginRegistry`(默认注册内置 14 插件,支持外部追加/替换/排序),`BaseLayer.plugins` 来自 registry。旧全局 `createPlugins` 作 deprecation wrapper(参考 source `ParserRegistry` 模式)。 **(☑ 已完成;`replace(name)` 基于元数据的精确替换归 2.2)**
8989
- 2.2 `ILayerPlugin` 补可选元数据:`name?: string` / `order?: number` / `initStage?: 'init'|'afterInit'`,便于声明式排序与调试(`addPlugin` 现仅 push 无序)。
9090
- 2.3 11 个 `protected get xxxService()` 懒 getter 收敛为一个 `protected services: LayerServices` 访问器对象(仍懒解析,零行为变化),减少 11 段重复样板;同时供 delegate 共享同一引用。
91-
- 2.4 修复阶段 1.2 发现的事件泄漏:`mapService.on('mapAfterFrameChange')` 匿名回调解构为具名方法 + `destroy()``mapService.off(...)`(strictly-better,零接口变化)。
91+
- 2.4 修复阶段 1.2 发现的事件泄漏:`mapService.on('mapAfterFrameChange')` 匿名回调解构为具名方法 + `destroy()``mapService.off(...)`(strictly-better,零接口变化)。 **(☑ 已完成)**
9292

9393
### 阶段 3 — 配置模型与类型强化
9494

packages/layers/src/core/BaseLayer.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -383,9 +383,7 @@ export default class BaseLayer<ChildLayerStyleOptions = {}>
383383
const { enableMultiPassRenderer, passes } = this.getLayerConfig();
384384
if (enableMultiPassRenderer && passes?.length && passes.length > 0) {
385385
// Tip: 兼容 multiPassRender 在 amap1 时存在的图层不同步问题 zoom
386-
this.mapService.on('mapAfterFrameChange', () => {
387-
this.renderLayers();
388-
});
386+
this.mapService.on('mapAfterFrameChange', this.onMapAfterFrameChange);
389387
}
390388

391389
this.postProcessingPassFactory = this.container.postProcessingPassFactory;
@@ -871,6 +869,8 @@ export default class BaseLayer<ChildLayerStyleOptions = {}>
871869
}
872870

873871
this.hooks.beforeDestroy.call();
872+
// 解除 mapAfterFrameChange 监听(修复历史匿名回调泄漏,与 onSourceUpdate 对称)
873+
this.mapService.off('mapAfterFrameChange', this.onMapAfterFrameChange);
874874
// 清除sources事件
875875
this.layerSource.off('update', this.onSourceUpdate);
876876

@@ -1263,6 +1263,18 @@ export default class BaseLayer<ChildLayerStyleOptions = {}>
12631263
}
12641264
};
12651265

1266+
/**
1267+
* `mapAfterFrameChange` 事件监听器(稳定实例引用,供 on/off 配对解绑)。
1268+
*
1269+
* 修复历史泄漏:原先在 init 内以 inline arrow 注册,destroy 时无法 off
1270+
* (引用不匹配),监听器随图层销毁后仍挂在 mapService 上泄漏。提取为具名
1271+
* 实例箭头方法后 on/off 统一引用,解绑真实生效。仅在 enableMultiPassRenderer
1272+
* + passes 下注册;destroy 内无条件 off(未注册时 off 为空操作,无害)。
1273+
*/
1274+
protected readonly onMapAfterFrameChange = (): void => {
1275+
this.renderLayers();
1276+
};
1277+
12661278
protected async initLayerModels() {
12671279
this.models.forEach((model) => model.destroy());
12681280
this.models = [];

0 commit comments

Comments
 (0)