gpt4 book ai didi

angularjs - 如何在 $compile 后删除由 Angular 指令创建的观察者

转载 作者:行者123 更新时间:2023-12-02 23:04:16 26 4
gpt4 key购买 nike

在以 Angular 多次打开模态时,我遇到了一些性能问题,并且我发现每次要求对话框服务创建新模态时,范围内的观察者数量都会急剧增加。

DialogService如下:

.factory("DialogService", function($q, $compile){
return {
toast:function(text){
Dialog.toast(text);
},
alert:function(text){
var deferred = $q.defer();
var d = Dialog.alert(text);
d.bind("hide", function(){
deferred.resolve();
});
return deferred.promise;
},
showModal:function(options){
var dialog = new Dialog(options);
dialog.show(function(){
var self = this;
if ( "scope" in options ) $compile(self.contentLayer)(options.scope);

options.scope.dismiss = function(){
dialog.hide();
}
});
if ( "hide" in options ){
dialog.bind("hide", options.hide);
}
},...

我面临的问题是,每次创建新模式时,当我使用指令时,它都会用新的观察者污染范围。

我通过添加观察者来解决大部分问题,当节点在我的自定义指令中被销毁时,这些观察者会删除观察者,但是 ng-repeat、ng-if 等......在模态内容中,每次调用 showModal 时都会添加观察者.

现在,我什至不确定我应该尝试什么方法。

现在我正在尝试删除使用对话框创建的观察者,但没有成功,因此我将不胜感激。

最佳答案

花了我一些时间,但最终我能够实现所需的行为

首先,最好从选项中传递的范围创建一个子范围,而不是像问题中所示直接将范围传递到对话服务。

这样,每次调用 $compile 来构建模态时,它就不会用监视和其他内容污染父作用域,并且在模态关闭时很容易销毁新创建的作用域。

服务的生成代码如下所示:

angular.module("app", [])
.factory("DialogService", function($q, $compile){
return {
toast:function(text){
Dialog.toast(text);
},
alert:function(text){
var deferred = $q.defer();
var d = Dialog.alert(text);
d.bind("hide", function(){
deferred.resolve();
});
return deferred.promise;
},
showModal:function(options){
var childScope;
var dialog = new Dialog(options);
dialog.show(function(){
var self = this;
if ( "scope" in options ){
var childScope = options.scope.$new();
$compile(self.contentLayer)(childScope);

options.scope.dismiss = function(){
dialog.hide();
}

dialog.bind("hide", function(){
childScope.$destroy();
});
}
});
if ( "hide" in options ){
dialog.bind("hide", options.hide);
}
},
confirm:function(text){
var deferred = $q.defer();
Dialog.confirm(text, function(){
deferred.resolve();
}, function(){
deferred.reject();
});
return deferred.promise;
}
}
})
;

额外

此时,我认为我解决了问题,但通过控制台日志,我可以看到每次打开新对话框时摘要循环仍在运行越来越多,尽管这次 watch 保持不变。

发生的事情是我有很多自定义指令;我非常小心地在每个指令的链接函数末尾添加以下几行,以避免在上述场景中出现性能问题:

var watchers = [
$scope.$watch(...),
$scope.$watch(...),
...
];
...
var observer = new MutationObserver(function(mutations) {
if (!document.body.contains($element[0])){
observer.disconnect();
dropdown.remove();
for ( var i = 0; i < watchers.length; i++ ){
watchers[i]();
}
$scope.$destroy();
return;
}
});
var config = { childList: true, subtree: false /*attributes: true, characterData: true*/ };
observer.observe(document.querySelector('body'), config);

看起来不错,对吧?好吧...大多数时候都有效,但是当我在指令中有类似的东西时(情况就是如此):

var clickHandler = function(event){
var isChild = $($element).has(event.target).length > 0 || $(dropdown).has(event.target).length > 0;
var isSelf = $element[0] == event.target || dropdown == event.target;

$scope.$apply(function(){
if (!isChild && !isSelf) {
$scope.mdSelectCtrl.dismiss();
}
});
}

$document.bind('click', clickHandler);

它在每个 $compile 上添加点击事件,点击事件会导致另一个摘要周期(即使范围已被破坏,此时不想深入挖掘);考虑到我可以轻松地在模态内容中使用相同的指令 10-15 次,这意味着每次调用 $compile 都会导致每次点击时运行大量摘要周期,从而降低性能。

解决这个问题的方法很简单:当指令的范围被破坏时删除 DOM 事件监听器:

var observer = new MutationObserver(function(mutations) {
if (!document.body.contains($element[0])){
observer.disconnect();
dropdown.remove();
for ( var i = 0; i < watchers.length; i++ ){
watchers[i]();
}
$scope.$destroy();
$document.unbind('click', clickHandler);
return;
}
});

注意

更改对话服务后,我意识到也许不需要 MutationObserver 来跟踪节点是否仍然存在,我可以使用:

$scope.$on("destroy")

但我今天不想更改任何代码来测试它。

关于angularjs - 如何在 $compile 后删除由 Angular 指令创建的观察者,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41351161/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com