MVVM的双向绑定

常见双向绑定的实现方法

(1)KnockoutJS 基于观察者模式的双向绑定;
(2)Ember 基于数据模型的;
(3)Angular 基于脏检查的双向绑定,使用主动遍历
(4)基于数据劫持的双向绑定
A.Vue现在使用的Object.defineProperty
B.ES6新增的Proxy。

基于数据劫持双向绑定的优点

无需显示调用

data.name = ‘Bingle’; 就能直接触发变更
不用像react使用setState显示调用;

可以精确得到变化数据

劫持属性的setter变化时可以得到newVal;否则只知道数据变了,具体需要大量的diff才能找出变化值;

基于Object.defineProperty双向绑定的两个缺点

无法监听数组的变化

根本原因:数组没有setter和getter,因为数组的长度不确定,可能会很大,出于性能考虑就没有setter和getter。
注:vue文档说可以检测数组变化,其实是使用了奇技淫巧,
实现代码思路如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
const aryMethods = ['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'];
const arrayAugmentations = [];

aryMethods.forEach((method)=> {

// 原生Array的原型方法
let original = Array.prototype[method];

// 将push, pop等封装好的方法定义在对象arrayAugmentations的属性上
// 注意:是属性而非原型属性
arrayAugmentations[method] = function () {
console.log('我被改变啦!');

// 调用对应的原生方法 并返回结果即新数组的长度
return original.apply(this, arguments);
};
});

let list = ['a', 'b', 'c'];
// 将我们要监听的数组的原型指针 指向上面定义的空数组对象
// 这个空数组的属性上定义了我们封装好的push等方法
list.__proto__ = arrayAugmentations;
//返回“我被改变了”和新的数组长度
list.push('d'); // 我被改变啦! 4

// 这里的list2没有被重新定义原型指针,所以就正常输出
let list2 = ['a', 'b', 'c'];
//只返回新数组长度
list2.push('d'); // 4

只能劫持对象的属性

我们需要对每个对象的每个属性进行遍历,如果属性值也是对象,就要深度遍历了。
所以要放大招了~

Proxy实现双向绑定的特点

严格讲proxy叫做代理,提供了一种机制能对外界访问进行过滤和改写,简单理解Proxy是Object.defineProperty的全方位加强版

优点

(1)直接监听整个对象,而不是属性
(2)可以监听数组的变化
(3)Proxy有多达13种拦截方法,很多都是Object.defineProperty不具备的。
(4)返回一个新的对象,不是像Object.defineProperty那样直接修改对象属性;符合immutable思想;
(5)新标准的性能红利,会受到浏览器厂商重点持续的心性能优化

缺点

兼容性问题,且无法用polyfill磨平,所以尤大才说要等到大版本3.0再用Proxy重写了。

参考文章:
掘金: 面试官: 实现双向绑定Proxy比defineproperty优劣如何?

以上

欢迎随时交流~

分享到:
Disqus 加载中...

如果长时间无法加载,请针对 disq.us | disquscdn.com | disqus.com 启用代理