Vue中对数据的监听主要是依靠Object.defineProperty来实现的,这种实现主要是针对key/value形式的对象,对数组中值的变化是无能为力的,那么该如何对数组中的数据进行监听呢,下面分析一下Vue对数组类型数据的监听方式。
 
一、首先考虑下数组变化的情况,主要有以下几种:
  1. 数组本身的赋值;
  2. 数组push等方法的使用导致的变化;
  3. 数组中的值变化导致的变化;
  4. 操纵数组长度导致的数组变化;
二、接下来对上面变化的情况依次进行分析:
 1.数组本身赋值的情况,这种情况显然跟对象的监听是一致的,直接使用defineProperty对数据进行监听就可以了,写个简单的例子看下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
<script src="./../../dist/vue.js"></script>
</head>
<body>
<div></div>
<div id="demo">
<div>
{{testArry}}
</div>
<input type="button" value="按钮" @click='clickHandler'/>
</div>
</body>
<script>
new Vue({
el:"#demo",
data: {
testArry: [1, 2, 3]
},
methods:{
clickHandler(){
this.testArry = [4, 5, 6]//直接赋值
//this.testArry[0] = 5;
//this.testArry = this.testArry;//改变数组中的值
//this.testArry.push(6);//调用方法      //this.testArry.length = 1;//改变长度
}
}
});
</script>
</html>

testArry是data(key、value形式)的一个属性,在初始化的时候 new Observer的时候会调用defineReactive进行正常监听,数据更新时通知订阅的watchers进行更新;

2.数组push等操作改变数据时想要监听到数据的变化是没办法继续通过defineProperty来实现的,需要直接监听push等方法,在调用方法时进行监听,所以考虑对数组原型上的方法进行hook,之后再将hook后的方法挂在到所要监听的数组数据的 __proto__上即可,过程如下:

首先hook数组原型方法(如push)

 var arrayProto = Array.prototype;
var arrayMethods = Object.create(arrayProto); var methodsToPatch = [
'push',
'pop',
'shift',
'unshift',
'splice',
'sort',
'reverse'
]; /**
* Intercept mutating methods and emit events
*/
methodsToPatch.forEach(function (method) {
// cache original method
var original = arrayProto[method];
def(arrayMethods, method, function mutator () {
var args = [], len = arguments.length;
while ( len-- ) args[ len ] = arguments[ len ]; var result = original.apply(this, args);
var ob = this.__ob__;
var inserted;
switch (method) {
case 'push':
case 'unshift':
inserted = args;
break
case 'splice':
inserted = args.slice(2);
break
}
if (inserted) { ob.observeArray(inserted); }
// notify change
ob.dep.notify();//通知watchers
return result
});
});

这里没有hook没有直接在Array.prototype上做,而是重新创建了一份原型对象 arrymethods 出来,既保留了方法的整个原型链,又避免了污染全局数组原型。

之后在观察数据时进行挂载:浏览器支持 __proto__ 那么直接挂载到数组的 __proto__上;不支持重新定义一份到数组本身;

var Observer = function Observer (value) {
this.value = value;
this.dep = new Dep();
this.vmCount = 0;
def(value, '__ob__', this);
if (Array.isArray(value)) {
if (hasProto) {
protoAugment(value, arrayMethods); //支持__proto__:此处直接进行挂载
} else {
copyAugment(value, arrayMethods, arrayKeys); //不支持__proto__:直接定义到数组上
}
this.observeArray(value);
} else {
this.walk(value);
}
}; /**
* Augment a target Object or Array by intercepting
* the prototype chain using __proto__
*/
function protoAugment (target, src) {
/* eslint-disable no-proto */
target.__proto__ = src;
/* eslint-enable no-proto */
} /**
* Augment a target Object or Array by defining
* hidden properties.
*/
/* istanbul ignore next */
function copyAugment (target, src, keys) {
for (var i = 0, l = keys.length; i < l; i++) {
var key = keys[i];
def(target, key, src[key]);
}
}

这里需要注意数据更新方面,Vue的数据更新都是通过依赖收集器(Dep实例)通知观察者(Watcher实例)来进行的。数组这里与对象的初始化不同,从性能上考虑挂载的方法在最开始就会且仅会初始化一次,那么就会导致dep实例的初始化与更新不在同一个作用域下。Vue的处理是给数组一个 __ob__的属性用来挂载数据,在push等操作触发hook 时再从数据的__ob__属性上取出 dep进行通知,这里处理的就很灵性了。

3.数组中的值变化,如果是对象或数组显然会递归处理直到基本类型,Vue对基本类型的数据是不进行观察的,主要也无法建立起监听,所以数组下标直接改变数组值这种操作不会触发更新;

4.数组长度的变化,无法监听,所以数组长度变化也不会触发更新;

三、总结:

Vue对数据的监听有两种,一种是数组本身的变化,直接通过Object.defineProperty实现;另一种是通过方法操纵数组,此时会hook原型上的方法建立监听机制;对于数组下标以及长度的变化没有办法直接建立监听,此时可以通过$set进行更新(会调用hook中的splice方法触发更新);对于数组长度变化导致的数据变化无法监听,如果想触发只能通过hack方式调用hook中的方法进行更新,如官方推荐的splice等;

玩转Vuejs--数组监听的更多相关文章

  1. js实现给一个数组监听

    $.when.apply(null, table).done(callback); table=[]是个数组,用上$.when.apply就可以监听完成后执行callback 方法 callback就 ...

  2. 玩转Android---事件监听篇---第2篇

    事件监听篇---第二篇 下面是各种常用控件的事件监听的使用 ①EditText(编辑框)的事件监听---OnKeyListener ②RadioGroup.RadioButton(单选按钮)的事件监听 ...

  3. vuejs 深度监听

    data: { obj: { a: 123 } }, 监听obj中a属性 watch: { 'obj.a': { handler(newName, oldName) { console.log('ob ...

  4. iOS: 使用KVO监听控制器中数组的变化

    一.介绍: KVO是一种能动态监听到属性值的改变的方式,使用场景非常广泛,这里我只讲如何监听控制器ViewController中数组的变化. 二.了解: 首先我们应该知道KVO是不能直接监听控制器Vi ...

  5. KVC和KVO实现监听容器类(数组等)的变化

    KVC,即Key-Value Coding,键值编码,简单地说,就是可以由key获取一个object对应的property.举个例子,如果一个对象object,它有一个属性item,你可以通过valu ...

  6. VueJs 监听 window.resize 方法

    Vuejs 本身就是一个 MVVM 的框架. 但是在监听 window 上的 事件 时,往往会显得 力不从心. 比如 这次是 window.resize 恩,我做之前也是百度了一下.看到大家伙都为这个 ...

  7. 在vue中使用watch监听对象或数组

    最近发现在vue中使用watch监听对象或者数组时,当数组或者对象只是单一的值改变时,并不会出发watch中的事件. 在找问题过程中,发现当数组使用push一类的方法时,会触发watch,如果只是单一 ...

  8. AngularJS监听数组变化

    我们在使用angualr的监听时候,业务的需要我们会去监听一个数组的某一个值得变化,再写逻辑代码.然而我们在使用$scope.$watch("",function(){ })时候会 ...

  9. 小程序中监听textarea或者input输入的值动态改变data中数组的对象的值

    Page({ data: { todoLists:[ { detail:"", date:"", location:"", priority ...

随机推荐

  1. Scrapyd

    scrapyd 安装 scrapyd-中心节点,子节点安装scrapyd-clientpip3 install scrapydpip3 install scrapyd-client scrapyd-c ...

  2. bzoj 3238

    后缀数组+单调栈的应用 首先我们研究一下这个表达式,可以发现前半部分与串的情况并没有关系,而只是跟串的长度有关,所以我们先把前半部分算出来: 于是我们只需计算出即可 那么可以发现,对于排名分别为i,j ...

  3. vs2010编译error_code

    C1083 : 现象: xxxxx.cpp clxx:fatal error C1083:无法打开源文件: “..\..\..\..\src\folder1\folder2\folder3\folde ...

  4. python内置模块之-logging

    logging 模块主要用于写日志 logging模块主要有如下几个组件 Logger  Logger对象提供应用程序可直接使用的接口 Handler Handler发送日志到适当的目的地 Filte ...

  5. MATLAB读视频报错 Unable to initialize the video obtain properties (videoreader in Matlab)

    这个bug卡了半天,这里记录一下 Error using VideoReader/init (line ) Could not read file due to an unexpected error ...

  6. LCA最近公共祖先

    不会 准备研究一波!!! #include<bits/stdc++.h> ; using namespace std; vector<int> g[maxn]; ][maxn] ...

  7. CSS---通向臃肿的道路(关于 “separation of concerns” (SoC)的原则)

    When it comes to CSS, I believe that the sacred principle of “separation of concerns” (SoC) has lead ...

  8. python田忌赛马

    一,简介 田忌赛马的故事大家都知道我就不展开说了,田忌能用同全面被碾压的马赢了齐威王(公子),我觉得这是十分具有智慧的.但是,如果说这里的条件改为:1,田忌的马比齐威王同等次的马弱一点但是比齐威王下一 ...

  9. jQuery 对象 等操作

    /////////////////////下面为文件夹重命名功能区///////////////////////// $(".wpul .rename").click(functi ...

  10. vue-countTo---简单好用的一个数字滚动插件

    vue-countTo是一个无依赖,轻量级的vue组件,可以自行覆盖easingFn. 你可以设置 startVal 和 endVal,它会自动判断计数或倒计时.支持vue-ssr.vue-count ...