diff --git a/src/core/observer/index.js b/src/core/observer/index.js index 35469aaf16a..44b686e6aea 100644 --- a/src/core/observer/index.js +++ b/src/core/observer/index.js @@ -158,11 +158,13 @@ export function defineReactive ( configurable: true, get: function reactiveGetter () { const value = getter ? getter.call(obj) : val - if (Dep.target) { + const targetWatcher = Dep.target + if (targetWatcher) { dep.depend() if (childOb) { + const needDepend = Array.isArray(value) && !targetWatcher.checkRelated(childOb.dep) childOb.dep.depend() - if (Array.isArray(value)) { + if (needDepend) { dependArray(value) } } diff --git a/src/core/observer/watcher.js b/src/core/observer/watcher.js index 725480d9215..a0715f99405 100644 --- a/src/core/observer/watcher.js +++ b/src/core/observer/watcher.js @@ -139,6 +139,13 @@ export default class Watcher { } } + /** + * Check if a dependency has been associated with the current watcher. + */ + checkRelated (dep: Dep) { + return this.newDepIds.has(dep.id) + } + /** * Clean up for dependency collection. */ diff --git a/test/unit/modules/observer/observer.spec.js b/test/unit/modules/observer/observer.spec.js index 5f075bccc95..81d2008df33 100644 --- a/test/unit/modules/observer/observer.spec.js +++ b/test/unit/modules/observer/observer.spec.js @@ -6,7 +6,7 @@ import { del as delProp } from 'core/observer/index' import Dep from 'core/observer/dep' -import { hasOwn } from 'core/util/index' +import { hasOwn, _Set as Set } from 'core/util/index' describe('Observer', () => { it('create on non-observables', () => { @@ -356,6 +356,33 @@ describe('Observer', () => { }) }) + it('Array\'s getter is triggered multiple times, the Watcher should only associate with Dep once.', () => { + const data = { + arr: [{}] + } + observe(data) + // mock a watcher! + const watcher = { + newDepIds: new Set(), + addDep (dep) { + this.newDepIds.add(dep.id) + dep.addSub(this) + }, + checkRelated: function (dep) { + return this.newDepIds.has(dep.id) + } + } + spyOn(watcher, 'checkRelated').and.callThrough() + Dep.target = watcher + data.arr + data.arr + Dep.target = null + expect(watcher.checkRelated.calls.count()).toBe(2) + const allCalls = watcher.checkRelated.calls.all() + expect(allCalls[0].returnValue).toBe(false) + expect(allCalls[1].returnValue).toBe(true) + }) + it('warn set/delete on non valid values', () => { try { setProp(null, 'foo', 1)