javascript 我是不是总是使用 Knockout 映射插件来做我的视图模型而过度使用它?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/7488208/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-26 00:17:13  来源:igfitidea点击:

Am I overusing the Knockout mapping plugin by always using it to do my viewmodel?

javascriptmvvmknockout.jsknockout-mapping-plugin

提问by Allen Rice

I'm still learning the proper usage of Knockout and I've found myself quickly getting away from ever typing ko.observablewhen setting up my viewmodel and instead just defining an object literal and passing it through the mapping plugin with something like

我仍在学习 Knockout 的正确用法,我发现自己ko.observable在设置视图模型时很快就不再打字了,而只是定义一个对象文字并通过映射插件将其传递给类似的东西

var viewModel = ko.mapping.fromJS(data);

or at the very least, something along the lines of stuffing all of my data into an attribute on the viewModel like so

或者至少,像这样将我的所有数据填充到 viewModel 上的属性中

var viewModel = { 
    ... events etc ... , 
    "data": ko.mapping.fromJS(data)
}

To be honest, the main reason I've been doing this is to get around having to type ko.observableand ko.observableArrayrepetitively. I'm just trying to figure out if this is a good approach and if there are any downsides to dropping the specific var x = ko.observable()declaration all together. Also, I'm doing this all on load, not in response to any ajax call etc, which from what I can tell, is what the mapping plugin was designed for.

说实话,我一直在做这个的主要原因是为了避开不必键入ko.observableko.observableArray重复。我只是想弄清楚这是否是一个好方法,以及将特定var x = ko.observable()声明放在一起是否有任何缺点。另外,我是在加载时做这一切的,而不是响应任何 ajax 调用等,据我所知,这是映射插件的设计目的。

In your work with knockout, do you still declare the observables manually, one by one, or have you gone with the mapping.fromJS method that I use? Are there any specific downsides to using the mapping plugin so frequently like this?

在你与淘汰赛的工作中,你是否仍然手动地一个一个地声明 observables,或者你是否使用了我使用的 mapping.fromJS 方法?像这样频繁地使用映射插件有什么具体的缺点吗?

Edit:

编辑:

Specific Example

具体例子

In this article, Steve sets up his viewModel by doing

在本文中,史蒂夫通过执行以下操作来设置他的 viewModel

var initialData = [ { ... } , { ... } ]; // json from the serializer
var viewModel = {
    gifts : ko.observableArray(initialData)
};

Normally, I'd just use ko.mapping.fromJSfor this situation as well, specifically to make sure the objects within the array are turned into observables as well. Looking at what he did, my approach seems like its overkill and adds a bit of unnecessary overhead.

通常,我也只是ko.mapping.fromJS在这种情况下使用,特别是为了确保数组中的对象也变成了可观察对象。看看他的所作所为,我的方法似乎有点矫枉过正,并增加了一些不必要的开销。

采纳答案by photo_tom

My suggestion to you would the same another questioned I just answered at https://stackoverflow.com/questions/7499133/mapping-deeply-hierarchical-objects-to-custom-classes-using-knockout-mapping-plug.

我对您的建议与我刚刚在https://stackoverflow.com/questions/7499133/mapping-deeply-hierarchical-objects-to-custom-classes-using-knockout-mapping-plug 上回答的问题相同

Your reasoning for using mapping plug-in is reasonable and the one that I use. Why type more code than you have to?

你使用映射插件的理由是合理的,也是我使用的。为什么要输入更多的代码?

In my experience with knockout (all of 4 months), I've found that the less I do manually and let the knockout routines do their thing, the better my apps seem to run. My suggestion is try the simplest approach first. If it doesn't meet your needs, look at how the simple approach is doing it's "thing" and determine what has to change to meet your needs.

根据我对淘汰赛(总共 4 个月)的经验,我发现我手动执行的操作越少,让淘汰赛例程完成它们的工作,我的应用程序似乎运行得越好。我的建议是先尝试最简单的方法。如果它不能满足您的需求,请查看简单的方法是如何做“事情”的,并确定必须更改哪些内容才能满足您的需求。

回答by Allen Rice

After using Knockout for a little longer, I've noticed that the mapping plugin has some additional options that give you much more fine grained control over the mapping process.

在使用 Knockout 一段时间后,我注意到映射插件有一些附加选项,可以让您对映射过程进行更精细的控制。

Control type and amount of properties generated

控制生成的属性的类型和数量

There are several ways to accomplish this, and I'll go over some, but the end result is that you end up with a lighter result from the mapping plugin because everything isn't observable.

有几种方法可以实现这一点,我将介绍其中的一些方法,但最终结果是您从映射插件中得到了更轻的结果,因为一切都是不可观察的。

Basically you leave everything that you don't think will change, as a normal property and only make observables out of the specific items that you want to observe.

基本上,您将您认为不会改变的所有内容都保留为正常属性,并且仅从您想要观察的特定项目中生成可观察对象。

Make mappingomit certain properties

makemapping省略某些属性

You can make the mapping plugin omit properties entirely from the end result by specifying things like ignoreor include. Both of these accomplish the same thing, just in opposite ways.

您可以通过指定诸如ignore或 之类的内容来使映射插件完全从最终结果中省略属性include。这两者完成相同的事情,只是以相反的方式。

Note: Samples are from the knockout.js mapping plugin documentation, comments added by me

注意:示例来自knockout.js mapping plugin文档,我添加的注释

Mapping Plugin Argument: include

映射插件参数: include

The following snippet will omit all propertiesfrom the source object other than those passed in via the includeargument.

以下代码段将省略源对象中除通过include参数传入的属性之外所有属性

// specify the specific properties to include as observables in the end result 
var mapping = {
    // only include these two properties
    'include': ["propertyToInclude", "alsoIncludeThis"]
}

// viewModel will now only contain the two properties listed above, 
//    and they will be observable
var viewModel = ko.mapping.fromJS(data, mapping);

Mapping Plugin Argument: ignore

映射插件参数: ignore

If you want to only omit certain propertiesfrom the source object, use the ignoreargument as shown below. It will make observables from all properties in the source object except for the specified properties.

如果您只想从源对象中省略某些属性,请使用ignore如下所示的参数。它将从源对象中的所有属性中生成 observable,除了指定的属性。

// specify the specific properties to omit from the result, 
//    all others will be made observable
var mapping = {
    // only ignore these two properties
    'ignore': ["propertyToIgnore", "alsoIgnoreThis"]
}

// viewModel will now omit the two properties listed above, 
//    everything else will be included and they will be an observable
var viewModel = ko.mapping.fromJS(data, mapping);

Control what properties are or are not made observable

控制哪些属性是可观察的或不可观察的

If you need to include properties but you don't think that they will need to be made observable (for whatever reason), the mapping plugin has something that can help.

如果您需要包含属性但您不认为它们需要被观察到(无论出于何种原因),映射插件可以提供帮助。

Mapping Plugin Argument: copy

映射插件参数: copy

If you want the mapping plugin to simply copy the plain properties and not make them observable, use this argument, as shown below.

如果您希望映射插件简单地复制普通属性而不是使它们可观察,请使用此参数,如下所示。

// tell the mapping plugin to handle all other properties normally, 
//    but to simply copy this property instead of making it observable
var mapping = {
    'copy': ["propertyToCopy"]
}
var viewModel = ko.mapping.fromJS(data, mapping);

Gain complete control over the mapping process

完全控制映射过程

If you want to have 100% control over what is created in the mapping process, including the ability to put closures and subscriptions in your objects, then you want to use the "create" option.

如果您想 100% 控制在映射过程中创建的内容,包括在对象中放置闭包和订阅的能力,那么您需要使用“创建”选项。

plain result with calculated properties

具有计算属性的简单结果

Here is an example where I was mapping data from an ajax call to an object with a resultsproperty. I didn't want anything observable and I just wanted a simple generated property that would be made of the other simple properties on the object. Maybe not the most compelling example but it demonstrates the functionality.

这是一个示例,其中我将数据从 ajax 调用映射到具有results属性的对象。我不想要任何可观察的东西,我只想要一个简单的生成属性,该属性将由对象上的其他简单属性组成。也许不是最引人注目的例子,但它展示了功能。

var searchMappingConfig = {
    // specific configuration for mapping the results property
    "results": {
                    // specific function to use to create the items in the results array
        "create": function (options) {
            // return a new function so we can have the proper scope/value for "this", below
            return new function () {

                // instead of mapping like we normally would: ko.mapping.fromJS(options.data, {}, this);
                // map via extend, this will just copy the properties from the returned json element to "this"
                // we'll do this for a more light weight vm since every last property will just be a plain old property instead of observable
                $.extend(this, options.data);

                // all this to add a vehicle title to each item
                this.vehicleTitle = this.Year + "<br />" + this.Make + " " + this.Model;
                }, this);
            };
        }
    }
}

subscriptions and closures and mapping, oh my

订阅和关闭以及映射,哦,我的

Another situation is if you want closures and subscriptions in your result. This example is too long to be included in its entirety but its for a vehicle make/model hierarchy. I wanted all the models (children) for a given make (parent) to be un-enabled if the model was un-enabled and I wanted this to be done with a subscription.

另一种情况是,如果您想在结果中关闭和订阅。此示例太长而无法完整包含,但它适用于车辆制造商/型号层次结构。如果模型未启用,我希望给定制造商(父)的所有模型(子)都未启用,并且我希望通过订阅来完成此操作。

// here we are specifying the way that items in the make array are created, 
//    since makes has a child array (Models), we will specify the way that 
//    items are created for that as well
var makesModelsMappingConfig = {
   // function that has the configuration for creating makes
   "create": function (options) {
      // return a new function so we can have the proper 
      //    scope/value for "this", below
      return new function () {

         // Note: we have a parent / child relationship here, makes have models. In the 
         //    UI we are selecting makes and then using that to allow the user to select 
         //    models. Because of this, there is going to be some special logic in here 
         //    so that all the child models under a given make, will automatically 
         //    unselect if the user unselects the parent make.

         // make the selected property a private variable so it can be closure'd over
         var makeIsSelected = ko.protectedComputed(false);

         // expose our property so we can bind in the UI
         this.isSelected = makeIsSelected;

         // ... misc other properties and events ...

         // now that we've described/configured how to create the makes, 
         //    describe/configure how to create the models under the makes
         ko.mapping.fromJS(options.data, {
            // specific configuration for the "Models" property                  
            "Models": {
               // function that has the configuration for creating items 
               //    under the Models property
               "create": function (model) {

                  // we'll create the isSelected as a local variable so 
                  //    that we can flip it in the subscription below, 
                  //    otherwise we wouldnt have access to flip it
                  var isSelected = ko.protectedComputed(false);

                  // subscribe to the parents "IsSelected" property so 
                  //    the models can select/unselect themselves
                  parentIsSelected.current.subscribe(function (value) {
                     // set the protected computed to the same 
                     //    value as its parent, note that this 
                     //    is just protected, not the actual value
                     isSelected(value);
                  });


                  // this object literal is what makes up each item 
                  //    in the Models observable array 
                  return {
                     // here we're returning our local variable so 
                     //    we can easily modify it in our subscription
                     "isSelected": isSelected,

                     // ... misc properties to expose 
                     //     under the item in the Model array ...

                  };
               }
            }
         }, this);
      };
   }
};

All in all, what I've found is that you rarely need 100% of an object that you'd pass to the plugin and you rarely need 100% of it to be observable. Dig in with the mapping configuration options and create all sorts of complex and simple objects. The idea is to only get everything you need, nothing more or less.

总而言之,我发现您很少需要 100% 的对象传递给插件,并且很少需要 100% 的对象是可观察的。深入了解映射配置选项并创建各种复杂和简单的对象。这个想法是只得到你需要的一切,不多也不少。

回答by Tom W Hall

Allen, my recent learning experience with Knockout.js has been similar to yours. We work with a deep hierarchical object graph from the server and I have defined explicit instantiable view model functions which preserve the basic structure of it.

Allen,我最近使用 Knockout.js 的学习经历与你的相似。我们使用来自服务器的深层层次对象图,并且我定义了显式可实例化的视图模型函数,这些函数保留了它的基本结构。

I began by defining each property explicitly as an observable on the relevant view model, but that quickly got out of hand. Also, a major reason for switching to using the mapping plugin was that we have to do frequent Ajax posts of the graph back to the server where it is merged with the persisted version, then validated on the server in such a way that numerous properties can change and collections be modified, and a new instance returned as the Ajax result where it has to be re-merged with the client representation. That became seriously difficult, and the mapping plugin helped big time by allowing the specification of identifiers for resolving adds / deletes / updates and to remap an updated graph onto the original.

我首先将每个属性明确定义为相关视图模型上的可观察对象,但很快就失控了。此外,切换到使用映射插件的一个主要原因是我们必须频繁地将图形发送回服务器,在那里它与持久版本合并,然后在服务器上以这样一种方式进行验证,即许多属性可以更改和集合被修改,一个新实例作为 Ajax 结果返回,它必须与客户端表示重新合并。这变得非常困难,映射插件通过允许标识符的规范来解决添加/删除/更新并将更新的图形重新映射到原始图形上,从而帮助了很多时间。

It also helped in the original graph creation through the use of the "create" option for sub view models. In each view model constructor I receive a reference to the parent view model plus the data with which to construct the child view model, then create further mapping options to create grandchildren from the passed-in child data.

通过使用子视图模型的“创建”选项,它还有助于原始图形的创建。在每个视图模型构造函数中,我收到对父视图模型的引用以及用于构造子视图模型的数据,然后创建进一步的映射选项以从传入的子数据创建孙子视图。

The only (slight) downside I recently found, as detailed in this question, is that when doing ko.mapping.toJSON it doesn't hook into any toJSON overrides you may have defined on the prototypes of your view models in order to exclude properties from serialization. I have been able to get around that by specifying ignore options in the unmapping, as recommended by Ryan Niemeyer in that post.

唯一的(轻微)下行空间我最近发现,在详细介绍了这个问题,就是在做ko.mapping.toJSON当它不挂接到你可能已经在你的视图模型的原型定义任何的toJSON覆盖,以排除性从序列化。我已经能够通过在取消映射中指定忽略选项来解决这个问题,正如 Ryan Niemeyer 在该帖子中所推荐的那样。

So in summary, I'll definitely be sticking with the mapping plugin. Knockout.js rules.

所以总而言之,我肯定会坚持使用映射插件。Knockout.js 规则。

回答by George Mavritsakis

A simpler but help-full add-on could be knockout-data-projections

一个更简单但很有帮助的附加组件可能是淘汰赛数据预测

Currently, it does not handle js to viewmodel mappings, but it handles quite well view model to JS mappings.

目前,它不处理 js 到视图模型的映射,但它可以很好地处理视图模型到 JS 的映射。