【问题标题】:Unbinding jQuery event handlers when a KnockoutJs view is unloaded卸载 KnockoutJs 视图时取消绑定 jQuery 事件处理程序
【发布时间】:2013-01-27 22:40:12
【问题描述】:

我有一个简单的应用程序,它有两个视图,一个“应用程序”模型和两个视图模型。我有兴趣研究如何取消绑定 jQuery 事件。

应用视图模型有这个属性:

var self = this;    
self.viewModel = ko.observable(null);

最重要的 HTML sn-ps 位于主模板中,即:

        <!-- ko if: viewModel -->
    <div data-bind="template:{name:viewModel().template, data:viewModel(), afterRender: viewModel().viewDidRender}"></div>
    <!-- /ko -->

在应用模型中,初始化后,加载视图模型。每个视图模型都有一个简单的模板属性,它是一个引用要呈现的 html 模板的字符串。模板声明中的数据项将 knockoutjs 上下文设置为当前视图模型。 afterRender 绑定确保调用 ViewModel 的 viewDidRender 方法(以容器 html 元素作为参数)。

这意味着我可以做到以下几点:

self.viewDidRender = function (parentElement) {
    self.containerElement = parentElement;

    $("html").on("click", function (event) {
        var elements = $(event.target).parents();
        for (var i = 0; i < elements.length; i++)
            if (elements[i] == self.containerElement[0] || elements[i] == self.containerElement) {
                alert("the target exists within the parent element");
                return;
            }
        self.open(false);
    });
};

有一个要求是我们拦截html元素的点击事件。如果事件发生在目标位于子视图的父元素内的位置,则忽略该事件,否则将“open”设置为 false(这实际上是您在 iOS 中使用 PopOvers 看到的轻度关闭机制的一部分)

我发现当我在两个主视图之间切换时(这个示例视图的 viewDidRender 每次渲染都会被调用),上面的 body click 处理程序被绑定了多次。

我认为这是 JavaScript 内存泄漏。应该去哪里清理这些绑定?我可以调用 $("html").off(...) 等...但是在哪里?模板绑定的 beforeRemove 事件不会为纯模板项调用......仅适用于 foreach 绑定。

可以在所有子视图模型中创建另一个方法,例如“viewWillHide”,因此每次将新视图推入应用模型的 ViewModel 属性时,我都可以尝试调用视图模型上的 viewWillHide 方法。这会奏效。但是,该应用程序比我在这里演示的要复杂。实际上存在视图模型和嵌套模板的层次结构。

手动操作是一种选择;模板绑定的 afterRender 没有对立面,这似乎很奇怪。

那你会怎么做?手动方法?即使它会导致整个视图模型层次结构发生级联变化,以便将 viewWillHide 方法从顶层(应用程序模型)向下传播到较小的视图模型。

谢谢

【问题讨论】:

  • 您没有在模板的 html 中使用敲除点击绑定是否有原因?然后在您的视图模型中进行点击处理?不过听起来是个棘手的问题。
  • 是的,因为视图模型是瞬态的。是否拦截点击只是视图和视图模型的关心。如果您无论如何都拦截点击,您不仅必须自己从应用程序模型级别向下传播它,而且还会出现其他问题,因此您将无休止地将专门模型的关注点插入应用程序更通用的架构中。我现在刚刚使用处理模式实现了它,如果没有人有更好的见解,我会在这里发布。

标签: knockout.js knockout-2.0


【解决方案1】:

尝试使用以下代码取消绑定您的 jQuery 事件

ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
    //Do cleanup
});

编辑:演示如何包装绑定的小技巧

http://jsfiddle.net/d6cJG/

问题是绑定到模板绑定的 div 上的 addDisposeCallback 不会触发回调,因为该元素永远不会改变,它里面的元素会随着模板改变而改变。

这里的回调将被调用http://jsfiddle.net/d6cJG/1/

【讨论】:

  • 不幸的是,如果我在模板绑定的“afterRender”方法中注册了这个处理回调,即使我正在注册,当模板更改或删除节点时也不会调用处理方法它针对在 afterRender 方法参数中提供的元素参数。我不知道为什么。但是,看起来这种方法确实用于绑定处理程序,所以我不知道它是否只是针对那个。
  • 您实际上应该更改游览代码以使用自定义绑定。渲染后和渲染前会在您的 ViewModel 中引入 DOM 依赖性
  • 我认为我不能将 html 元素绑定到瞬态、多态的视图模型,并且绑定的目标可能在 html 不知道的视图模型层次结构中向下 n 级元素声明。
  • 在您的情况下,只需将模板绑定包装在自定义绑定中并将渲染函数抽象为该绑定即可。我看不出为什么 addDisposeCallback 不能在绑定之外工作。
  • 有趣...好的,我将研究为此自定义绑定的想法
猜你喜欢
  • 2010-10-22
  • 1970-01-01
  • 2017-10-05
  • 1970-01-01
  • 2012-08-05
  • 1970-01-01
  • 2010-10-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多