双向数据绑定如何实现的
- 脏检查: angular.js内部会维护一个序列,将所有需要监控的属性放在这个序列中,当发生某些特定事件时,angular会调用$digest方法,这个方法的内部的逻辑就是遍历所有的watcher,对被监控的属性做对比,如果发生变化,调用响应的handler。缺点很明显,性能很差,特别当监控数量达到一个数量级的时候
- 观察机制 Obeject.observe(),缺点支持度不行
- 封装属性访问器 vue.js
- 访问器属性
- ![enter description here][2]
- 访问器属性比较特殊,读取或设置访问器属性的值,实际上是调用其内部特性:get和set
- vue采用的是数据劫持+发布者订阅者方式实现双向数据绑定的,通过object.defineProperty()劫持各个属性的get和set方法,在数据变动时发布消息给订阅者,触发相应的监听回调
vue实现双向数据绑定的代码
function compile (node, vm) {
var reg = /\{\{(.*)\}\}/;
if (node.nodeType === 1) {
var attr = node.attributes;
for (var i=0;i<attr.length;i++) {
if (attr[i].nodeName == 'v-model') {
var name = attr[i].nodeValue;
node.addEventListener('keyup', function (e) {
vm[name] = e.target.value;
});
node.value = vm.data[name];
node.removeAttribute('v-model');
}
}
}
if (node.nodeType === 3) {
if (reg.test(node.nodeValue)) {
var name = RegExp.$1;
name = name.trim();
new Watcher(vm, node, name);
}
}
}
function Watcher (vm, node, name) {
Dep.target = this;
this.name = name;
this.node = node;
this.vm = vm;
this.update();
Dep.target = null;
}
Watcher.prototype = {
update: function () {
this.get();
this.node.nodeValue = this.value;
},
get: function () {
this.value = this.vm[this.name];
}
}
function nodeToFragment (node, vm) {
var flag = document.createDocumentFragment();
var child;
while (child = node.firstChild) {
compile(child, vm);
flag.appendChild(child);
}
return flag;
}
function defineReactive (obj, key, val) {
var dep = new Dep();
Object.defineProperty(obj, key, {
get: function () {
if (Dep.target) {
dep.addSub(Dep.target);
}
return val;
},
set: function (newVal) {
if (newVal === val) return;
val = newVal;
dep.notify();
}
});
}
function observe (obj, vm) {
Object.keys(obj).forEach(function (key) {
defineReactive(vm, key, obj[key]);
});
}
function Vue (options) {
this.data = options.data;
var data = this.data;
observe(data, this);
var id = options.el;
var dom = nodeToFragment(document.getElementById(id), this);
document.getElementById(id).appendChild(dom);
}
function Dep () {
this.subs = [];
}
Dep.prototype = {
addSub: function (sub) {
this.subs.push(sub);
},
notify: function () {
this.subs.forEach(function (sub) {
sub.update();
});
}
}
var vm = new Vue({
el: 'app',
data: {
text: 'hello world'
}
})