数据绑定的秘诀是什么?
What's the secret to data-binding?
对于大多数 JS 框架和库而言,它们带来的价值通常表现为关于如何构建应用程序的新结构(Backbone、React)或有效提升能量的新想法他们提供的语言 (Angular),或者只是他们提供的方法都经过充分测试、快速且非常方便 (jQuery)。
通常他们提供的想法和方法是 JavaScript 的非常简单的用法,但背后有一个非常聪明的团队,他们找到有趣的方法来做事,你可以通过思考并得到可靠的猜测胆量如何运作。
但是,我一直无法想通双向绑定 JS 模型以查看组件的能力。这个功能的核心秘诀是什么?从用户输入更改内部变量很简单,但反过来呢?当 JS 变量发生变化时,您如何能够 "know" 以立即更新显示?肯定不能轮询,那又怎样?
每当您的 JS 运行 块被 angular 触发时,它将在该块完成执行时 运行 一个摘要循环。这基本上会检查所有可能更改的值,并且需要更新视图。
如果 angular 没有触发代码,那么它不会知道某些事情可能发生了变化,因此您的绑定可能会不同步。例如,如果你 运行 像这样
setTimeout(function() {$scope.myValue = '123'});
Angular 不会知道 myValue
已更改,它实际上不会更新视图。这就是为什么 Angular 拥有自己的服务来完成所有事情。例如$timeout
或 $http
.
如果您有一些 Angular 不知道的回调函数,那么您可以通过调用 $scope.$apply()
手动告诉它检查更改
我可以在 Backbone 中讲述它是如何完成的,它对数据绑定的看法相对较低。
它是 1. 控制属性 setter 方法的库 2. 在属性更改时调用回调函数(例如通过调度事件)以更新 UI.
基本的伪代码是这样的:
class Model:
method set(name, value):
if value != this.attributes[name]
this.triggerEvent('change', name, value)
this.attributes[name] = value
m = new Model()
someInputWidget.onEvent('userChangedInput', function(value) {
m.set(someInputWidget.name, value)
})
m.onEvent('change', function(name, value) {
getInputWidgetByName(name).setValue(value)
})
Backbone 不对 UI 进行任何数据绑定,但您可以参考 Backbone 的 annotated source 以了解实际的事件调度实现。
有几种方法可以做到这一点。 Object.observe 很棒,但缺乏很好的支持。您也可以轮询值,保留对象的第二个副本以进行比较。您还可以编写自己的显式 set/get 方法来像 backbone 那样更新模型。
我经常使用的一种巧妙方法是使用 getters/setters 使模型与 dom 保持同步:
//a demo "model" of data:
model = {
name: "Fred"
};
function change(k,v){alert([k,v]);} // a stand-in change monitor for demo
// iterate model and replace values with getter/setter combos:
Object.keys(model).forEach(function(key) {
var val = model[key];
delete model[key];
Object.defineProperty(model, key, {
get: function() {
return val;
},
set: function(v) {
val = v;
change(key, val);
} //call change upon setting
});
change(key, val); //update view "onload"
}); // alerts "Fred";
//update model (fires change() with "name" and "sally" arguments:
model.name="sally"; // alerts "sally";
更改功能非常简单,对于您的情况,应该只查找绑定到键的元素。
这里的优点是您不需要特殊的自定义 CRUD 方法,您可以像 1999 年一样通过赋值修改对象属性。它也不会轮询,并且可以在 IE9 和任何其他 ES5 环境中一直正常工作。这是无需自定义方法即可绑定 JS>DOM (afaik) 的最简单方法。
它确实有一些限制:嵌套对象很难get/set,你不能一次做整个对象,你只能"watch"基元。数组也是一个问题:您不能真正将 expando 属性替换为 getters/setters 而没有副作用。但是,在相对平坦的 JSON 安全数据集合上,get/set 很有魅力,不需要复杂的库即可运行。
查看使用此方法的完整示例:http://pagedemos.com/xg3szbguqnwu/4
对于大多数 JS 框架和库而言,它们带来的价值通常表现为关于如何构建应用程序的新结构(Backbone、React)或有效提升能量的新想法他们提供的语言 (Angular),或者只是他们提供的方法都经过充分测试、快速且非常方便 (jQuery)。
通常他们提供的想法和方法是 JavaScript 的非常简单的用法,但背后有一个非常聪明的团队,他们找到有趣的方法来做事,你可以通过思考并得到可靠的猜测胆量如何运作。
但是,我一直无法想通双向绑定 JS 模型以查看组件的能力。这个功能的核心秘诀是什么?从用户输入更改内部变量很简单,但反过来呢?当 JS 变量发生变化时,您如何能够 "know" 以立即更新显示?肯定不能轮询,那又怎样?
每当您的 JS 运行 块被 angular 触发时,它将在该块完成执行时 运行 一个摘要循环。这基本上会检查所有可能更改的值,并且需要更新视图。
如果 angular 没有触发代码,那么它不会知道某些事情可能发生了变化,因此您的绑定可能会不同步。例如,如果你 运行 像这样
setTimeout(function() {$scope.myValue = '123'});
Angular 不会知道 myValue
已更改,它实际上不会更新视图。这就是为什么 Angular 拥有自己的服务来完成所有事情。例如$timeout
或 $http
.
如果您有一些 Angular 不知道的回调函数,那么您可以通过调用 $scope.$apply()
我可以在 Backbone 中讲述它是如何完成的,它对数据绑定的看法相对较低。
它是 1. 控制属性 setter 方法的库 2. 在属性更改时调用回调函数(例如通过调度事件)以更新 UI.
基本的伪代码是这样的:
class Model:
method set(name, value):
if value != this.attributes[name]
this.triggerEvent('change', name, value)
this.attributes[name] = value
m = new Model()
someInputWidget.onEvent('userChangedInput', function(value) {
m.set(someInputWidget.name, value)
})
m.onEvent('change', function(name, value) {
getInputWidgetByName(name).setValue(value)
})
Backbone 不对 UI 进行任何数据绑定,但您可以参考 Backbone 的 annotated source 以了解实际的事件调度实现。
有几种方法可以做到这一点。 Object.observe 很棒,但缺乏很好的支持。您也可以轮询值,保留对象的第二个副本以进行比较。您还可以编写自己的显式 set/get 方法来像 backbone 那样更新模型。
我经常使用的一种巧妙方法是使用 getters/setters 使模型与 dom 保持同步:
//a demo "model" of data:
model = {
name: "Fred"
};
function change(k,v){alert([k,v]);} // a stand-in change monitor for demo
// iterate model and replace values with getter/setter combos:
Object.keys(model).forEach(function(key) {
var val = model[key];
delete model[key];
Object.defineProperty(model, key, {
get: function() {
return val;
},
set: function(v) {
val = v;
change(key, val);
} //call change upon setting
});
change(key, val); //update view "onload"
}); // alerts "Fred";
//update model (fires change() with "name" and "sally" arguments:
model.name="sally"; // alerts "sally";
更改功能非常简单,对于您的情况,应该只查找绑定到键的元素。 这里的优点是您不需要特殊的自定义 CRUD 方法,您可以像 1999 年一样通过赋值修改对象属性。它也不会轮询,并且可以在 IE9 和任何其他 ES5 环境中一直正常工作。这是无需自定义方法即可绑定 JS>DOM (afaik) 的最简单方法。
它确实有一些限制:嵌套对象很难get/set,你不能一次做整个对象,你只能"watch"基元。数组也是一个问题:您不能真正将 expando 属性替换为 getters/setters 而没有副作用。但是,在相对平坦的 JSON 安全数据集合上,get/set 很有魅力,不需要复杂的库即可运行。
查看使用此方法的完整示例:http://pagedemos.com/xg3szbguqnwu/4