Fork me on GitHub

vue-监听数组变化

Vue.js观察数组变化主要通过以下7个方法(push、pop、shift、unshift、splice、sort、reverse)

怎么实现?

通过对data数据中数组的这7个方法进行重新包装(注意只是data数据中的数组)

为什么不直接对Array.prototype的原型方法进行重新包装?

因为不应该过多地去影响全局

代码实现

 1 const patchArray = (function () {
 2     const methodsToPatch = [
 3         'push',
 4         'pop',
 5         'shift',
 6         'unshift',
 7         'splice',
 8         'reverse',
 9         'sort'
10     ];
11 
12 //设置对象属性的工具方法
13     function def (obj, key, val) {
14         Object.defineProperty(obj, key, {
15             value: val,
16             enumerable: true,
17             writable: true,
18             configurable: true
19         });
20     }
21 
22     const arrayProto = Array.prototype, //缓存Array的原型
23         arrayMethods = Object.create(arrayProto); //继承Array的原型
24 
25     methodsToPatch.forEach(function (method, index) {
26         def(arrayMethods, method, function (...args) {
27             //首先调用Array原型的方法
28             const res = arrayProto[method].apply(this, args);
29             //data中每个数组都有一个__ob__的私有属性指向创建的Observer实例(有兴趣看看源码中的observe方法,这里不详述)
30             const ob = this.__ob__;
31 
32             let inserted = null;
33 
34             //记录插入的值
35             switch(method) {
36                 case 'push':
37                 case 'unshift':
38                     inserted = args;
39                     break;
40                 case 'splice':
41                     inserted = args.slice(2);
42                     break;
43             }
44 
45             if (inserted) {
46                 //如果是调用了push、unshift、splice,则尝试对新插入的值进行响应式绑定,因为插入的值有可能是对象(Object)或者数组(Array)
47                 ob && ob.observeArray(inserted);
48             }
49 
50             console.log('数组发生改变了');
51 
52             //向所有依赖发送通知,告诉它们数组的值发生变化了
53             ob && ob.dep.notify();
54             return res;
55         });
56     });
57 
58     return function (target) {
59         //看看浏览器支不支持__proto__这个属性,通过改变__proto__的值,可以设置对象的原型
60         if ('__proto__' in {}) {
61             //将数组的原型指向arrayMethods,这样当数组调用上述的7个方法时,其实是调用arrayMethods中的方法而不是调用Array.prototype中的方法
62             target.__proto__ = arrayMethods;
63         } else {
64             //如果浏览器不支持__proto__,则设置数组对应的属性,这样当数组调用上述的7个方法时,其实是调用数组对应属性指向的方法
65             for (let i = 0, l = methodsToPatch.length; i < l; i++) {
66                 let key = methodsToPatch[i];
67                 def(target, key, arrayMethods[key]);
68             }
69         }
70     }
71 })();
72 
73 //测试
74 let arr = [1, 2, 3];
75 patchArray(arr);
76 arr.push(4);

 

posted @ 2018-03-13 21:10  Alex_Zheng  阅读(15348)  评论(0编辑  收藏  举报