Javascript 淘汰JS不清除组件

Javascript 淘汰JS不清除组件,javascript,knockout.js,requirejs,web-component,Javascript,Knockout.js,Requirejs,Web Component,所以这里有一个奇怪的淘汰赛问题,我以前从未真正遇到过 我正在开发一个大量使用淘汰组件的应用程序 在应用程序的一部分中,我有一个编辑器页面,它是从JSON驱动的后端动态构建的,并根据后端数据告诉前端页面的内容,使用许多小部件填充前端页面 范例 后端可能会发送 [{"widget": "textBox"},{"widget": "textBox"},{"widget": "comboBox"},{"widget": "checkBox"}] 这将导致前端构建一个包含 <html>

所以这里有一个奇怪的淘汰赛问题,我以前从未真正遇到过

我正在开发一个大量使用淘汰组件的应用程序

在应用程序的一部分中,我有一个编辑器页面,它是从JSON驱动的后端动态构建的,并根据后端数据告诉前端页面的内容,使用许多小部件填充前端页面

范例 后端可能会发送

[{"widget": "textBox"},{"widget": "textBox"},{"widget": "comboBox"},{"widget": "checkBox"}]
这将导致前端构建一个包含

<html>
  ....
  <textbox></textbox>
  <textbox></textbox>
  <combobox></combobox>
  <checkbox></checkbox>
  ....
</html>
其中“数据”如上面的JSON所示

现在所有这些都很有效,但这里有一个奇怪的部分

如果我必须“重新加载”页面,我只需

pageComponentsToDisplay = ko.observableArray([]);
要清除阵列,因此,我的所有组件也会像预期的那样从页面中消失,但是当我加载新数据时,再次使用:

pageComponentsToDisplay(data);
我像预期的那样在屏幕上看到了我的新组件,但旧组件似乎仍然存在,并在内存中处于活动状态,即使它们不可见

我知道控件仍然存在的原因是,当我发出一条PubSub消息要求控件提供一些状态信息时,所有控件都会回复

在我看来,当我清除数组时,当KO清除视图模型时,它实际上似乎并没有销毁旧副本

此外,如果我再次刷新,我会得到3组组件响应,再次刷新,结果是4组,这会像预期的那样不断增加

这是我第一次在击倒中遇到这种行为,我多年来一直使用这种模式,没有任何问题

如果您想全面了解整个项目的设置方式,我在github页面上提供了一个框架布局示例:

如果有人对这里可能发生的事情有任何想法,我很乐意听听

最后一点,我实际上是在使用Typescript开发所有这些,但由于这是一个运行时问题,所以我将从JS的角度对其进行记录

问候 肖蒂

更新1 因此,在进一步挖掘之后(由于cl3m的回答,我有了一点“新思维”),我更进一步了

在我最初的帖子中,我确实提到我正在使用Ryan Niemeyer出色的PubSub扩展来制作淘汰赛“ko邮箱”

事实证明,我的“组件”正在被处理和拆除,但为响应邮箱而创建的订阅处理程序却没有

结果是,VM(或者更具体地说是订阅在VM中使用的值)与邮箱订阅处理程序一起保存在内存中

这意味着,当我的主机广播请求组件值的消息时,保持的引用将响应,随后是可见的活动组件

我现在需要做的是找到一种处理这些订阅的方法,这是因为我直接使用postbox,并且没有将它们分配给模型中的可观察对象,这意味着我实际上没有一个var或object引用来针对它们

探索仍在继续

更新2
查看我对以下问题的自我回答。

我不确定这是否有帮助,但根据我的评论,以下是我如何在我的
自定义绑定中使用
ko.utils.domNodeDisposal.addDisposeCallback()
。也许有一种方法可以在淘汰
组件中使用它:

ko.bindingHandlers.tooltip = {
    init: function(element, valueAccessor) {
      $(element).tooltip(options);
      ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
        $(element).tooltip('destroy');
      });
   }
}

更多阅读

问题似乎是由于当实际组件处于活动状态时,邮箱设置的订阅上挂起了敲除

在我的例子中,我使用邮箱纯粹是作为一个消息传递平台,所以我所做的就是

ko.postbox.subscribe("foo", function(payload) { ... });
一直以来,因为我只是以这种方式使用单次订阅,所以我从来没有关注邮箱订阅调用返回的值

我这样做,只是因为在我创建的许多组件中,有一个通用的API,它们都使用,但它们都以不同的方式响应,所以我所需要的只是一个简单的方法,这就是当调用特定于组件但不特定于应用程序的处理程序时要做的事情

然而,事实证明,当您以这种方式使用邮箱时,并没有可观察到的目标,因此也并没有可处理的内容。(您没有保存报税表,因此您没有任何可处理的内容)

敲除和邮箱文档没有提到的是,Postbox.subscribe的返回值是一个通用的敲除订阅函数,通过将它的返回值分配给模型中的属性,您就有了调用其上可用功能的方法,其中一个函数提供了“dispose”实例,它不仅从组件集合中删除组件的物理显示,而且还确保连接到组件的任何订阅或事件处理程序也被正确地删除

再加上在注册时可以将dispose处理程序传递给VM,最终的解决方案是确保执行以下操作

/// <amd-dependency path="text!application/components/pagecontrols/template.html" />
define(["require", "exports", "knockout", 'knockout.postbox', "text!application/components/pagecontrols/template.html"], function (require, exports, ko, postbox) {
    var Template = require("text!application/components/pagecontrols/template.html");
    var ViewModel = (function () {
        function ViewModel(params) {
            var _this = this;
            this.someDataBoundVar = ko.observable("");
            this.mySubscriptionHandler = ko.postbox.subscribe("foo", function(){
              // do something here to handle subscription message
            });
        }
        ViewModel.prototype.somePublicFunction = function () {
            postbox.publish("SomeMessage", { data: "some data" });
        };
        return ViewModel;
        ViewModel.prototype.dispose = function () {
          this.mySubscriptionHandler.dispose();
        };
        return ViewModel;
    })();
    return { viewModel: ViewModel, template: Template, dispose: this.dispose };
});
//
定义([“require”、“exports”、“knockout”、“knockout.postbox”、“text!application/components/pagecontrols/template.html]”),函数(require、exports、ko、postbox){
var Template=require(“text!application/components/pagecontrols/Template.html”);
var ViewModel=(函数(){
函数视图模型(参数){
var_this=这个;
this.someDataBoundVar=ko.可观察(“”);
this.mySubscriptionHandler=ko.postbox.subscribe(“foo”,function()){
//在此处执行某些操作以处理订阅消息
});
}
ViewModel.prototype.somePublicFunction=函数(){
publish(“SomeMessage”,{data:“somedata”});
};
返回视图模型;
ViewModel.prototype.dispose=函数(){
this.mySubscriptionHandler.dispose();
};
返回视图模型;
})();
返回{viewModel:viewModel,template:template,d
ko.bindingHandlers.tooltip = {
    init: function(element, valueAccessor) {
      $(element).tooltip(options);
      ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
        $(element).tooltip('destroy');
      });
   }
}
ko.postbox.subscribe("foo", function(payload) { ... });
/// <amd-dependency path="text!application/components/pagecontrols/template.html" />
define(["require", "exports", "knockout", 'knockout.postbox', "text!application/components/pagecontrols/template.html"], function (require, exports, ko, postbox) {
    var Template = require("text!application/components/pagecontrols/template.html");
    var ViewModel = (function () {
        function ViewModel(params) {
            var _this = this;
            this.someDataBoundVar = ko.observable("");
            this.mySubscriptionHandler = ko.postbox.subscribe("foo", function(){
              // do something here to handle subscription message
            });
        }
        ViewModel.prototype.somePublicFunction = function () {
            postbox.publish("SomeMessage", { data: "some data" });
        };
        return ViewModel;
        ViewModel.prototype.dispose = function () {
          this.mySubscriptionHandler.dispose();
        };
        return ViewModel;
    })();
    return { viewModel: ViewModel, template: Template, dispose: this.dispose };
});