使用 $scope.$destroy 解决内存泄漏但破坏指令
Using $scope.$destroy solves memory leak but breaks directive
我在 TypeScript AngularJS 应用程序中有一个非常动态的子指令 运行ning。模板和控制器根据给定情况需要执行的操作在 运行 时间附加,并且模板本身包含许多指令。我需要能够在页面上显示多个指令,因此我在每个指令之间使用了一个独立的范围。我有另一个指令负责跟踪这些子指令中的哪些应该在任何给定时间出现在页面上(称为父指令)。
如果我需要添加一个新的子项,我会在该父指令中为其创建模板,确定我要将其附加到的元素并使用:
var compiledDirective = this.$compile(myTemplate)(scope.$new(true, scope));
angular.element(".parent").append(compiledDirective);
_.defer(() => {
scope.$apply();
});
如果我加载应用程序,它工作正常,我可以看到它创建了 运行 的每个子项,正如我所期望的那样。当我需要删除这些子项中的 any/all 时,该父项也会在以下函数中进行处理:
var destroyChild = (identifier: string) {
this.$timeout(() => {
var elem = angular.element(".parent").find(`li[ident='${identifier}']`);
elem.scope().$destroy();
elem.remove();
_.defer(() => {
});
});
}
现在,所有元素都按预期自行删除,如果我查看 Batarang,我会看到指令从可用范围中消失。我在子指令中有一个 $destroy 事件的事件处理程序,所以当指令被销毁时我可以看到一条控制台消息,并且它在这个销毁函数 运行s.
之后按预期出现
这里的问题是我需要能够根据父页面上的其他活动添加更多这些子指令。在这个 destroy activity 之后,我发现当我第二次重新执行这个过程时,范围没有完全正确地加载 - 相反,我现在看到了为子指令创建的一些范围,但是我发现一些绑定不再更新(例如,我注意到当子指令中的范围值发生变化时 ng-hide 不再更新)因此该指令最终无法使用。
一般来说,我假设逻辑中只是存在一个错误,我需要去寻找它,而且很可能存在,除了在调试它时,我发现在处理的父函数中销毁子指令,如果我注释掉该行:
elem.scope().$destroy();
所有范围都保留在 Batarang 中,所有 DOM 元素都如您所料消失,所以这看起来有点内存泄漏,除非我 运行 触发器为了使指令再次加载,它们加载时没有问题(现在我只是在 Batarang 视图中看到许多范围,这些范围永远不会清除)。
什么会导致这种行为,因为 $destroying 指令的独立作用域会破坏指令的未来使用,但将它留在那里使其稍后可以工作,但会导致内存泄漏?有没有其他方法可以在不使用 $destroy 的情况下清除这些作用域?
编辑:
经过进一步调查,我发现当第一个指令被创建时,它有一个适当创建的隔离范围,但是在制作后续指令时,它们的隔离范围是在 Batarang 中创建的,但是如果我实际上用 [=37 进行查找=]('lookup-their-individual-idents').scope() 所报告的 ID 实际上是父级的 ID,因此当我销毁第一个时,它会按预期进行清理。当我销毁第二个时,它实际上清除了父作用域(以及所有子作用域),所以这就是为什么它不能准确地放置在父作用域上的原因。
所以,我希望我只需要弄清楚当我将它们添加到 DOM 时如何将隔离范围适当地绑定到指令,其余的将自行解决。
一旦我开始确切地查看在 Batarang 中应该创建和销毁哪些指令时,问题就变成了 apparent。第一个指令总是有一个独立的范围,后续指令的 none 宁愿,它们都会继承 parent 范围,所以当它们全部被销毁时, parent范围将与他们一起去。
相反,虽然我上面显示的代码用于创建页面上 none 的新指令,但我需要更新实际负责在页面上放置多个指令的代码parent 指令让它为每个新的 children 创建独立的作用域,例如:
var child = this.templateGenerator.addChild(applicableData);
var compiledChild = this.$compile(child)(scope.$new(true));
element.append(compiledChild);
现在,如果我查看每个指令,它们都指向 Batarang 中的预期范围。更重要的是,当我销毁它们并在页面上放置新指令时,它们也会按预期加载,所以这已经解决了问题。
我在 TypeScript AngularJS 应用程序中有一个非常动态的子指令 运行ning。模板和控制器根据给定情况需要执行的操作在 运行 时间附加,并且模板本身包含许多指令。我需要能够在页面上显示多个指令,因此我在每个指令之间使用了一个独立的范围。我有另一个指令负责跟踪这些子指令中的哪些应该在任何给定时间出现在页面上(称为父指令)。
如果我需要添加一个新的子项,我会在该父指令中为其创建模板,确定我要将其附加到的元素并使用:
var compiledDirective = this.$compile(myTemplate)(scope.$new(true, scope));
angular.element(".parent").append(compiledDirective);
_.defer(() => {
scope.$apply();
});
如果我加载应用程序,它工作正常,我可以看到它创建了 运行 的每个子项,正如我所期望的那样。当我需要删除这些子项中的 any/all 时,该父项也会在以下函数中进行处理:
var destroyChild = (identifier: string) {
this.$timeout(() => {
var elem = angular.element(".parent").find(`li[ident='${identifier}']`);
elem.scope().$destroy();
elem.remove();
_.defer(() => {
});
});
}
现在,所有元素都按预期自行删除,如果我查看 Batarang,我会看到指令从可用范围中消失。我在子指令中有一个 $destroy 事件的事件处理程序,所以当指令被销毁时我可以看到一条控制台消息,并且它在这个销毁函数 运行s.
之后按预期出现这里的问题是我需要能够根据父页面上的其他活动添加更多这些子指令。在这个 destroy activity 之后,我发现当我第二次重新执行这个过程时,范围没有完全正确地加载 - 相反,我现在看到了为子指令创建的一些范围,但是我发现一些绑定不再更新(例如,我注意到当子指令中的范围值发生变化时 ng-hide 不再更新)因此该指令最终无法使用。
一般来说,我假设逻辑中只是存在一个错误,我需要去寻找它,而且很可能存在,除了在调试它时,我发现在处理的父函数中销毁子指令,如果我注释掉该行:
elem.scope().$destroy();
所有范围都保留在 Batarang 中,所有 DOM 元素都如您所料消失,所以这看起来有点内存泄漏,除非我 运行 触发器为了使指令再次加载,它们加载时没有问题(现在我只是在 Batarang 视图中看到许多范围,这些范围永远不会清除)。
什么会导致这种行为,因为 $destroying 指令的独立作用域会破坏指令的未来使用,但将它留在那里使其稍后可以工作,但会导致内存泄漏?有没有其他方法可以在不使用 $destroy 的情况下清除这些作用域?
编辑: 经过进一步调查,我发现当第一个指令被创建时,它有一个适当创建的隔离范围,但是在制作后续指令时,它们的隔离范围是在 Batarang 中创建的,但是如果我实际上用 [=37 进行查找=]('lookup-their-individual-idents').scope() 所报告的 ID 实际上是父级的 ID,因此当我销毁第一个时,它会按预期进行清理。当我销毁第二个时,它实际上清除了父作用域(以及所有子作用域),所以这就是为什么它不能准确地放置在父作用域上的原因。
所以,我希望我只需要弄清楚当我将它们添加到 DOM 时如何将隔离范围适当地绑定到指令,其余的将自行解决。
一旦我开始确切地查看在 Batarang 中应该创建和销毁哪些指令时,问题就变成了 apparent。第一个指令总是有一个独立的范围,后续指令的 none 宁愿,它们都会继承 parent 范围,所以当它们全部被销毁时, parent范围将与他们一起去。
相反,虽然我上面显示的代码用于创建页面上 none 的新指令,但我需要更新实际负责在页面上放置多个指令的代码parent 指令让它为每个新的 children 创建独立的作用域,例如:
var child = this.templateGenerator.addChild(applicableData);
var compiledChild = this.$compile(child)(scope.$new(true));
element.append(compiledChild);
现在,如果我查看每个指令,它们都指向 Batarang 中的预期范围。更重要的是,当我销毁它们并在页面上放置新指令时,它们也会按预期加载,所以这已经解决了问题。