延迟加载自定义绑定

时间:2015-09-30 14:21:22

标签: javascript knockout.js

我正在处理单页应用程序,我们非常广泛地使用Knockout。我们目前有一个可以点击的项目列表,一旦这样做,他们就会将一些内容加载到一个模态容器中。下图说明了触发各种内容的不同项目:

enter image description here

这些容器的内容差异很大,并且可以在多个选项卡上分布许多不同的自定义绑定。图像中的项目非常简单,只使用Knockout组件,但是当我们开始显示模态内容时,它们对JavaScript更加沉重,因此使用绑定。

我最近添加了延迟加载组件所需的JavaScript和HTML模板,这非常有用。我不得不使用自定义组件加载器,因为我们不想使用需要或类似的AMD模块加载器的各种原因。

现在我面临着与定制淘汰赛绑定相同的问题,因为我预计随着这个产品的扩展,我们可以很容易地结束100个绑定。不幸的是,似乎没有一种显而易见的方式以像组件一样的懒惰方式加载自定义绑定,而且我试图弄清楚是否有这样做的方法,以及最好的方法方式会。请注意,我一直都不知道绑定的名称,有时我可能希望根据可观察的名称动态加载它们。

到目前为止我唯一能找到的注意事项是,有一个ko.getBindingHandler()函数可以被覆盖,但它需要同步加载绑定处理程序。

我想过尝试这样做的方法,但它使用组件,感觉就像是实现我最终目标的一种非常落后的方式。它是这样的:

替换通常的自定义绑定:

<div data-bind="lineChart: $data"/> 

<div data-bind="component { name: compName, params: { vm: $data } }"/>

然后我使用自定义组件加载器,它实际上只是加载绑定处理程序JavaScript,并基本上用自定义绑定写出占位符div

var bindingLoader = {
   getConfig: function(name, callback) {
      if(name.startsWith("binding-")) {
         callback({ binding: name.replace("binding-", ""), jsUrl: "/bindings/" + name });
         return;
      }
      callback(null);
   },
  loadComponent(name, componentConfig, callback) {
     var obj = { };
     obj.template = '<div data-bind="' + componentConfig.name + ': $data"/>';
     $.ajax({ url: componentConfig.jsUrl, dataType: "text" })
       .done(function(data)) {
           (new Function(data))();
           callback(obj);
     });
  }
}

我确信必须有更好的方法来实现这一目标,但我现在无法想到任何其他选择。

2 个答案:

答案 0 :(得分:1)

我也在Github上回答了这个问题。

@Jeroen是正确的,没有内置的异步加载自定义绑定的方法。但任何约束都可以“懒散地”#34;执行自己的操作,这是component绑定的作用。通过覆盖ko.getBindingHandler,我们可以检测尚未加载的绑定,然后启动加载过程,然后返回一个包装器绑定处理程序,在它加载后应用绑定。

var originalGetBindingHandler = ko.getBindingHandler;
ko.getBindingHandler = function (bindingKey) {
    // If the binding handler is already known about then return it now
    var handler = originalGetBindingHandler(bindingKey);
    if (handler) {
        return handler;
    }

    if (bindingKey.startsWith("dl-")) {
        bindingKey = bindingKey.replace("dl-", "");
        if (ko.bindingHandlers[bindingKey]) {
            return ko.bindingHandlers[bindingKey];
        }

        // Work out the URL at which the binding handler should be loaded
        var url = customBindingUrl(bindingKey);

        // Load the binding from the URL
        var loading = $.getScript(url);

        return ko.bindingHandlers["dl-" + bindingKey] = {
            init: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
                // Once the binding is loaded, apply it to the element
                loading.done(function() { 
                    var binding = {};
                    binding[bindingKey] = valueAccessor;
                    ko.applyBindingAccessorsToNode(element, binding);
                });
                // Assumes that all dynamically loaded bindings will want to control descendant bindings
                return { controlsDescendantBindings: true };
            }
        }
    }
};

http://jsfiddle.net/mbest/e718a123/

答案 1 :(得分:0)

AFAIK:不,懒惰加载自定义绑定没有通用的方法。

但是有很多选择,但我们不能推荐任何特定选项,因为它们在很大程度上取决于上下文。总结一些例子:

  • 如果可能,您可以在组件内部使用这些绑定,并懒惰地加载组件;
  • 根据你的绑定处理程序的作用,它本身可以延迟加载,直到最后需要的时间(例如在malloc()你只需要注册一个实际加载的事件回调你要加载的东西);
  • 如果您正确使用init绑定,则在需要之前不会评估其中的任何自定义绑定。 if绑定也是如此,除非那些项目存在,否则不会对数组项应用自定义绑定。
  • 只有在您准备好时,才能将foreach调用到DOM的特定部分。

等等。但同样,你的问题与广泛接壤。使用实际场景创建一个(或多个?)新问题,告诉我们为什么/如何需要自定义绑定来懒散加载,并告诉我们您尝试了哪些方法以及它们为什么不起作用。