Просмотр исходного кода

avoid false watcher triggering on deep/Array watchers during digest (fix #1014)

Evan You 11 лет назад
Родитель
Сommit
7b1c2f4872
3 измененных файлов с 66 добавлено и 4 удалено
  1. 1 1
      src/instance/scope.js
  2. 20 3
      src/watcher.js
  3. 45 0
      test/unit/specs/misc_spec.js

+ 1 - 1
src/instance/scope.js

@@ -166,7 +166,7 @@ exports._unproxy = function (key) {
 exports._digest = function () {
   var i = this._watchers.length
   while (i--) {
-    this._watchers[i].update()
+    this._watchers[i].update(true) // shallow updates
   }
   var children = this.$children
   i = children.length

+ 20 - 3
src/watcher.js

@@ -48,6 +48,9 @@ function Watcher (vm, expOrFn, cb, options) {
     this.setter = res.set
   }
   this.value = this.get()
+  // state for avoiding false triggers for deep and Array
+  // watchers during vm._digest()
+  this.queued = this.shallow = false
 }
 
 var p = Watcher.prototype
@@ -161,12 +164,22 @@ p.afterGet = function () {
 /**
  * Subscriber interface.
  * Will be called when a dependency changes.
+ *
+ * @param {Boolean} shallow
  */
 
-p.update = function () {
+p.update = function (shallow) {
   if (!config.async) {
     this.run()
   } else {
+    // if queued, only overwrite shallow with non-shallow,
+    // but not the other way around.
+    this.shallow = this.queued
+      ? shallow
+        ? this.shallow
+        : false
+      : !!shallow
+    this.queued = true
     batcher.push(this)
   }
 }
@@ -181,13 +194,17 @@ p.run = function () {
     var value = this.get()
     if (
       value !== this.value ||
-      _.isArray(value) ||
-      this.deep
+      // Deep watchers and Array watchers should fire even
+      // when the value is the same, because the value may
+      // have mutated; but only do so if this is a
+      // non-shallow update (caused by a vm digest).
+      ((_.isArray(value) || this.deep) && !this.shallow)
     ) {
       var oldValue = this.value
       this.value = value
       this.cb(value, oldValue)
     }
+    this.queued = this.shallow = false
   }
 }
 

+ 45 - 0
test/unit/specs/misc_spec.js

@@ -176,4 +176,49 @@ describe('Misc', function () {
     })
   })
 
+  it('should not trigger deep/Array watchers when digesting', function (done) {
+    var spy1 = jasmine.createSpy('deep')
+    var spy2 = jasmine.createSpy('Array')
+    var spy3 = jasmine.createSpy('test')
+    var spy4 = jasmine.createSpy('deep-mutated')
+    var vm = new Vue({
+      el: 'body',
+      data: {
+        obj: {},
+        arr: [],
+        obj2: {}
+      },
+      watch: {
+        obj: {
+            handler: spy1,
+            deep: true
+        },
+        arr: spy2,
+        // if the watcher is watching the added value,
+        // it should still trigger properly
+        test: {
+          handler: spy3,
+          deep: true
+        },
+        // if the object is in fact mutated, it should
+        // still trigger.
+        obj2: {
+          handler: spy4,
+          deep: true
+        }
+      }
+    })
+    var test = []
+    var obj2 = vm.obj2
+    vm.$add('test', test)
+    obj2.$add('test', 123)
+    Vue.nextTick(function () {
+      expect(spy1).not.toHaveBeenCalled()
+      expect(spy2).not.toHaveBeenCalled()
+      expect(spy3).toHaveBeenCalledWith(test, undefined)
+      expect(spy4).toHaveBeenCalledWith(obj2, obj2)
+      done()
+    })
+  })
+
 })