Knockout Google 地图:组件与自定义绑定处理程序

Knockout Google Map: Component vs. Custom Binding Handler

当我 google“Knockout Google 地图”时,我发现相当多的基于 KO 的 Google 地图实现。我能够找到的所有这些都采用了使用自定义绑定处理程序的方法,而我最初打算将其实现为 Knockout 组件。

示例:

任何人都可以指出我正确的方向为什么在这里比 KO 组件更喜欢自定义绑定处理程序

我计划的用例是这样的:

我正在实现一个包含地址搜索结果列表的页面。到目前为止的列表是一个 KO 组件,每个列表条目都由另一个 KO 组件生成,列表组件在 foreach 绑定中重复调用该组件。在这个搜索结果列表旁边,我需要一个 google 地图来显示地图中的结果条目。列表、列表条目和地图之间也会有相当多的交互。

这是我目前得到的:

var GMap = function () {
    var self = this;

    var initMap = function() {
        var map = new google.maps.Map(document.getElementById('map'), {
            zoom: 13,
            center: {lat: 51.4387974, lng: 6.9922915}
        });
    };
  
    initMap();
};
$(document).ready(function() {
  ko.components.register('gmap', {
    viewModel: GMap,
    template: { element: 'gmap' }
  });
  ko.applyBindings();
});
#map {
  height: 400px;
  width: 600px;
}
<script src="https://maps.googleapis.com/maps/api/js?v=3.22"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.0/knockout-min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<gmap></gmap>
<template id="gmap">
  <div id="map"></div>
</template>

组件和自定义处理程序是完全不同的东西。

自定义绑定

基本上 custom binding 可以访问:

  • 使用它的 HTML 组件
  • 绑定值(提供给绑定的表达式)
  • 元素中的所有其他绑定
  • 元素的绑定上下文,从中可以访问$root$parent

它的定义包括两个函数:

  • init:允许进行初始设置,如初始化小部件、设置事件处理程序等
  • update:在init之后调用。在那一刻,您可以通过绑定、所有元素绑定、上下文等访问属性(包括可观察的属性)。这会创建 subscriptios,当任何访问的 observable 发生变化时,它会调用更新。

因此,当您需要直接与 DOM 元素交互时,应使用自定义绑定,例如修改其属性、初始化小部件、订阅事件等

组件

一个组件完全不同。当你定义一个组件时,你必须定义:

  • 一个模板,它是一组 DOM 个元素,通常带有绑定
  • 一个视图模型(通常是构造函数或工厂)

当您使用该组件时:

  • 视图模型已实例化
  • 模板已加载
  • 视图模型绑定到模板

因此,组件允许重用视图模型和模板

所以,有什么区别?

自定义绑定可以直接访问 DOM 元素,允许与它们交互、订阅事件、修改属性等

一个组件只是一个视图模型,以及一组 DOM 绑定到该特定视图模型的元素。

因此,在 Google 地图的情况下,它需要初始化一个小部件(地图)并与地图事件交互,并响应可观察的属性变化,您永远不能使用组件,因为组件不允许与 DOM 元素直接交互。 (记住是一堆 HTML 具有绑定的元素,以及相应的视图模型,其中不能包含与这些元素交互的任何逻辑)。

自定义绑定通常适用于单个元素(尽管它可以处理其子元素,如 foreach)。对于 Google 地图,您只需要显示地图的元素。

组件通常是一组或多或少复杂的 DOM 元素,“从外部”无法访问这些元素。与主视图模型的唯一通信是通过参数完成的。该组件不能直接与 DOM 元素交互:它必须通过 ko 绑定来实现。

因此,对于 Google 地图的情况,很明显您需要自定义绑定。

只有当您想模块化或重用一组 DOM 元素和相关的视图模型时,才有意义创建组件,它还可以包括访问 Web 服务等功能(通过 AJAX),进行计算(可能通过使用计算的可观察量),等等。例如,可以使用一个组件来实现购物车,其中包括:

  • 用于显示购物车中商品的 DOM 元素(可能是 HTML table 和一些控件)
  • 用于修改购物车内容的控件(例如删除元素或更改数量)
  • 显示总计、税收等的视图模型
  • 存储购物车以供日后使用或付款的功能(可以是 ajax 调用服务)

在这种情况下,购物车将有一个视图模型,其中包括计算出的可观察值(以显示总计和税收)、删除项目或修改数量、存储或支付等的功能。以及一组具体的 DOM 元素,具有此视图模型的绑定,即 HTML 以显示购物车并与之交互。

在 Google 地图的情况下,如果没有自定义绑定的帮助或 对其他非 ko 脚本的 hacky 使用,则无法使用组件 .

如果您想在地图旁边显示地点列表并修改该列表,您可以使用一个组件,其中包括一个带有列表和相关功能的视图模型,以及一个包含带有 Google 地图自定义绑定。这是有道理的:viewmodel + 几个元素。

结论

这一切都意味着自定义绑定通常与绑定的 DOM 元素有深度交互,而组件与元素有更高级别的交互,必须通过绑定来完成。

因此,它们在非常不同的层面上发挥着作用。您无法比较或互换它们。

如果你坚持这样做,你可以创造一个野兽f 一个行为类似于组件的绑定,因为您可以完全控制元素,并且可以完全访问视图模型,但这比组件更难实现。也可能以某种深奥的方式反过来做。

绑定

Binding,无论是否是习俗,都是一个非常简单的概念,涵盖两件事:

  1. UI 元素的 属性 发生变化,因此它应该更新对象 (ViewModel)
  2. 对象 (ViewModel) 的 属性 发生变化,因此它应该更新 UI 元素。

从上面如果只实现了1个,它被称为单向绑定(因为如果你改变UI,它会更新对象而不是另一个方式)。如果1和2都实现了,就叫Two Way Binding.

因此,如果您认为需要执行此操作,则任何时候都需要使用绑定,如果框架没有您需要的绑定,则需要自定义绑定。

很可能,您所说的地图需要像上面这样的东西。它确实做到了,因为作者在第一段中这样说:

Concretely, you can learn how to make the maps marker part of the View and automatically change its position any time when the ViewModel behind changes.

看,作者在上面讲了2:当ViewModel改变时,改变UI元素的位置。

组件

A component 是一个概念,即拥有一个可重复使用的项目,该项目可能具有 UI 但不一定,并且使其工作所需的所有代码都打包在一起它。这样它就可以重复使用。例如,它可能只是一个仅允许数字的输入 UI 元素。它所需的所有代码都与 UI 元素一起打包。

现在和它一起打包的代码可能与绑定相关的代码。如果他们使用的框架没有他们需要的绑定,它甚至可能有自定义绑定。此外,它可能还有与绑定无关的附加代码。

此外,一个组件可以有一个或多个 UI 元素。具有多个元素的组件的一个很好的例子是消息框。

总结

绑定和组件是不同的东西。一个组件可能在其中有绑定,或者它可能有其他代码来使其工作,或者两者兼而有之。

就您所说的地图而言,它们只添加了一个功能:对 ViewModel 中的变化做出反应。它不是一个组件,因为它不是独立的和可重复使用的。

他们本可以使用组件来完成。但是,如果他们这样做并说它是一个 KO 组件,它可能仍然有 KO 特定的绑定代码与它一起打包,以及 ViewModel 和所有需要的 UI 元素。