Hacktoberfest 2026:维护者为十月标记出来的 issue,仍然开放、适合新手。 浏览 Hacktoberfest issue

Complete Omission of block.destroy() in Blocks Collection

未关闭 适合新手
#3,029 0 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

@Minhal128 已经在做这个了。

开始于 2026年9月24日。

  • #3034 来自 @Minhal128 —— 未关闭

评估

难度
2/5
预计耗时
1-3 小时
新手友好度
84/100
Issue 类型
缺陷
描述清晰度
描述清楚
活跃度
活跃
技术栈
typescript
领域
frontend

调研方向

读取 src/components/blocks.ts 中的 insert、replace、remove 和 removeAll,然后将它们的生命周期调用与 src/components/block/index.ts 中的 Block.destroy() 进行比较。当每条移除或替换 block 的路径都执行所需的清理,同时保留现有的 removal hook 行为时,即表示完成。

由索引模型根据 Issue 内容生成。

描述

bug

1. Complete Omission of block.destroy() in Blocks Collection

📍 Affected Locations
  • src/components/blocks.ts (Lines 200–205, 230–240, 285–295, 300–306)
  • src/components/block/index.ts (Lines 688–698)
🔍 Deep Technical Diagnosis

The Block class defines an essential cleanup method in src/components/block/index.ts:

// src/components/block/index.ts: Lines 688-698
public destroy(): void {
  this.holder.removeEventListener('change', this.handleSelectChange);
  this.unwatchBlockMutations();
  this.removeInputEvents();

  super.destroy();

  if (_.isFunction(this.toolInstance.destroy)) {
    this.toolInstance.destroy();
  }
}

However, in the core collection manager src/components/blocks.ts, block.destroy() is never called anywhere:

1. When removing a block:
// src/components/blocks.ts: Lines 285-295
public remove(index: number): void {
  if (isNaN(index)) {
    index = this.length - 1;
  }

  this.blocks[index].holder.remove();
  this.blocks[index].call(BlockToolAPI.REMOVED); // <-- Calls removed hook, BUT NEVER destroy()!
  this.blocks.splice(index, 1);
}
2. When clearing all blocks (removeAll / blocks.clear()):
// src/components/blocks.ts: Lines 300-306
public removeAll(): void {
  this.workingArea.innerHTML = '';
  this.blocks.forEach((block) => block.call(BlockToolAPI.REMOVED)); // <-- NEVER destroy()!
  this.blocks.length = 0;
}
3. When updating a block (replace / blocks.update()):
// src/components/blocks.ts: Lines 230-240
public replace(index: number, block: Block): void {
  const prevBlock = this.blocks[index];
  prevBlock.holder.replaceWith(block.holder);
  this.blocks[index] = block; // <-- Neither removed NOR destroy() is called!
}
4. When inserting with replace (insert(..., replace = true)):
// src/components/blocks.ts: Lines 200-204
if (replace) {
  this.blocks[index].holder.remove();
  this.blocks[index].call(BlockToolAPI.REMOVED); // <-- destroy() skipped!
}
💥 Real-World Impact
  1. Leaked MutationObservers: this.unwatchBlockMutations() is never executed. Each deleted block leaves an active MutationObserver observing an orphaned DOM node.
  2. Leaked Input Event Listeners: this.removeInputEvents() is never executed; all keydown, paste, and input handlers remain bound to inputs.
  3. Broken Tool Teardown: Third-party plugins that implement destroy() (e.g., stopping video streams, tearing down WebGL/Three.js canvases, clearing setInterval timers, closing WebSocket channels) never have their destroy() hook fired when a block is deleted or updated!
  4. Over a long editing session or dynamic updates in collaborative editors (e.g., Yjs), hundreds of orphaned blocks accumulate in memory.
💡 Proposed Solution & Patch
src/components/blocks.ts
@@ -201,6 +201,7 @@ export default class Blocks {
     if (replace) {
       this.blocks[index].holder.remove();
       this.blocks[index].call(BlockToolAPI.REMOVED);
+      this.blocks[index].destroy();
     }

     const deleteCount = replace ? 1 : 0;
@@ -236,6 +237,8 @@ export default class Blocks {
     const prevBlock = this.blocks[index];

     prevBlock.holder.replaceWith(block.holder);
+    prevBlock.call(BlockToolAPI.REMOVED);
+    prevBlock.destroy();

     this.blocks[index] = block;
   }
@@ -290,6 +293,7 @@ export default class Blocks {
     this.blocks[index].holder.remove();

     this.blocks[index].call(BlockToolAPI.REMOVED);
+    this.blocks[index].destroy();

     this.blocks.splice(index, 1);
   }
@@ -301,6 +305,9 @@ export default class Blocks {
     this.workingArea.innerHTML = '';

-    this.blocks.forEach((block) => block.call(BlockToolAPI.REMOVED));
+    this.blocks.forEach((block) => {
+      block.call(BlockToolAPI.REMOVED);
+      block.destroy();
+    });

     this.blocks.length = 0;
   }

主要语言
TypeScript
星标
32k
派生
2.2k
平均合并
2 天 1 小时
30 天内合并 PR
1

环境准备

这个项目没有提供开发容器、Dockerfile 或贡献指南,环境需要你自己搭建:先看它的 README,通用步骤见我们的新手贡献指南。

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

codex-team/editor.js 的其他 Issue

查看 codex-team/editor.js 的全部 Issue

相似的 Issue

更多 TypeScript Issue

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。