Skip to content

Commit c0d22e8

Browse files
committed
simplify watcher
1 parent 09c8877 commit c0d22e8

File tree

12 files changed

+40
-191
lines changed

12 files changed

+40
-191
lines changed

src/api/data.js

+5-15
Original file line numberDiff line numberDiff line change
@@ -70,28 +70,18 @@ exports.$delete = function (key) {
7070

7171
exports.$watch = function (exp, cb, deep, immediate) {
7272
var vm = this
73-
var key = deep ? exp + '**deep**' : exp
74-
var watcher = vm._userWatchers[key]
7573
var wrappedCb = function (val, oldVal) {
7674
cb.call(vm, val, oldVal)
7775
}
78-
if (!watcher) {
79-
watcher = vm._userWatchers[key] =
80-
new Watcher(vm, exp, wrappedCb, {
81-
deep: deep,
82-
user: true
83-
})
84-
} else {
85-
watcher.addCb(wrappedCb)
86-
}
76+
var watcher = new Watcher(vm, exp, wrappedCb, {
77+
deep: deep,
78+
user: true
79+
})
8780
if (immediate) {
8881
wrappedCb(watcher.value)
8982
}
9083
return function unwatchFn () {
91-
watcher.removeCb(wrappedCb)
92-
if (!watcher.active) {
93-
vm._userWatchers[key] = null
94-
}
84+
watcher.teardown()
9585
}
9686
}
9787

src/directive.js

+13-28
Original file line numberDiff line numberDiff line change
@@ -78,27 +78,16 @@ p._bind = function (def) {
7878
}
7979
}
8080
: function () {} // noop if no update is provided
81-
// use raw expression as identifier because filters
82-
// make them different watchers
83-
var watcher = this.vm._watchers[this.raw]
84-
// v-repeat always creates a new watcher because it has
85-
// a special filter that's bound to its directive
86-
// instance.
87-
if (!watcher || this.name === 'repeat') {
88-
watcher = this.vm._watchers[this.raw] = new Watcher(
89-
this.vm,
90-
this._watcherExp,
91-
update, // callback
92-
{
93-
filters: this.filters,
94-
twoWay: this.twoWay,
95-
deep: this.deep
96-
}
97-
)
98-
} else {
99-
watcher.addCb(update)
100-
}
101-
this._watcher = watcher
81+
var watcher = this._watcher = new Watcher(
82+
this.vm,
83+
this._watcherExp,
84+
update, // callback
85+
{
86+
filters: this.filters,
87+
twoWay: this.twoWay,
88+
deep: this.deep
89+
}
90+
)
10291
if (this._initValue != null) {
10392
watcher.set(this._initValue)
10493
} else if (this.update) {
@@ -182,17 +171,13 @@ p._checkParam = function (name) {
182171

183172
p._teardown = function () {
184173
if (this._bound) {
174+
this._bound = false
185175
if (this.unbind) {
186176
this.unbind()
187177
}
188-
var watcher = this._watcher
189-
if (watcher && watcher.active) {
190-
watcher.removeCb(this._update)
191-
if (!watcher.active) {
192-
this.vm._watchers[this.raw] = null
193-
}
178+
if (this._watcher) {
179+
this._watcher.teardown()
194180
}
195-
this._bound = false
196181
this.vm = this.el = this._watcher = null
197182
}
198183
}

src/instance/compile.js

+3-9
Original file line numberDiff line numberDiff line change
@@ -118,13 +118,9 @@ exports._destroy = function (remove, deferCleanup) {
118118
// splicing the directives
119119
this._unlinkFn(true)
120120
}
121-
// teardown all user watchers.
122-
var watcher
123-
for (i in this._userWatchers) {
124-
watcher = this._userWatchers[i]
125-
if (watcher) {
126-
watcher.teardown()
127-
}
121+
i = this._watchers.length
122+
while (i--) {
123+
this._watchers[i].teardown()
128124
}
129125
// remove reference to self on $el
130126
if (this.$el) {
@@ -152,8 +148,6 @@ exports._cleanup = function () {
152148
this._data.__ob__.removeVm(this)
153149
this._data =
154150
this._watchers =
155-
this._userWatchers =
156-
this._watcherList =
157151
this.$el =
158152
this.$parent =
159153
this.$root =

src/instance/init.js

+1-3
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,7 @@ exports._init = function (options) {
2020
this.$root = options._root || this
2121
this.$ = {} // child vm references
2222
this.$$ = {} // element references
23-
this._watcherList = [] // all watchers as an array
24-
this._watchers = {} // internal watchers as a hash
25-
this._userWatchers = {} // user watchers as a hash
23+
this._watchers = [] // all watchers as an array
2624
this._directives = [] // all directives
2725

2826
// a flag to avoid this being observed

src/instance/scope.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -123,9 +123,9 @@ exports._unproxy = function (key) {
123123
*/
124124

125125
exports._digest = function () {
126-
var i = this._watcherList.length
126+
var i = this._watchers.length
127127
while (i--) {
128-
this._watcherList[i].update()
128+
this._watchers[i].update()
129129
}
130130
var children = this._children
131131
i = children.length

src/watcher.js

+6-40
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,15 @@ var uid = 0
2323

2424
function Watcher (vm, expression, cb, options) {
2525
this.vm = vm
26-
vm._watcherList.push(this)
26+
vm._watchers.push(this)
2727
this.expression = expression
28-
this.cbs = [cb]
28+
this.cb = cb
2929
this.id = ++uid // uid for batching
3030
this.active = true
3131
options = options || {}
3232
this.deep = !!options.deep
3333
this.user = !!options.user
34+
this.twoWay = !!options.twoWay
3435
this.deps = []
3536
this.newDeps = []
3637
// setup filters if any.
@@ -174,46 +175,11 @@ p.run = function () {
174175
) {
175176
var oldValue = this.value
176177
this.value = value
177-
var cbs = this.cbs
178-
for (var i = 0, l = cbs.length; i < l; i++) {
179-
cbs[i](value, oldValue)
180-
// if a callback also removed other callbacks,
181-
// we need to adjust the loop accordingly.
182-
var removed = l - cbs.length
183-
if (removed) {
184-
i -= removed
185-
l -= removed
186-
}
187-
}
178+
this.cb(value, oldValue)
188179
}
189180
}
190181
}
191182

192-
/**
193-
* Add a callback.
194-
*
195-
* @param {Function} cb
196-
*/
197-
198-
p.addCb = function (cb) {
199-
this.cbs.push(cb)
200-
}
201-
202-
/**
203-
* Remove a callback.
204-
*
205-
* @param {Function} cb
206-
*/
207-
208-
p.removeCb = function (cb) {
209-
var cbs = this.cbs
210-
if (cbs.length > 1) {
211-
cbs.$remove(cb)
212-
} else if (cb === cbs[0]) {
213-
this.teardown()
214-
}
215-
}
216-
217183
/**
218184
* Remove self from all dependencies' subcriber list.
219185
*/
@@ -224,14 +190,14 @@ p.teardown = function () {
224190
// we can skip this if the vm if being destroyed
225191
// which can improve teardown performance.
226192
if (!this.vm._isBeingDestroyed) {
227-
this.vm._watcherList.$remove(this)
193+
this.vm._watchers.$remove(this)
228194
}
229195
var i = this.deps.length
230196
while (i--) {
231197
this.deps[i].removeSub(this)
232198
}
233199
this.active = false
234-
this.vm = this.cbs = this.value = null
200+
this.vm = this.cb = this.value = null
235201
}
236202
}
237203

test/unit/specs/api/data_spec.js

+5-16
Original file line numberDiff line numberDiff line change
@@ -89,23 +89,12 @@ describe('Data API', function () {
8989
vm.a = 2
9090
nextTick(function () {
9191
expect(spy).toHaveBeenCalledWith(4, 3)
92-
// reuse same watcher
93-
var spy2 = jasmine.createSpy()
94-
var unwatch2 = vm.$watch('a + b.c', spy2)
95-
expect(vm._watcherList.length).toBe(1)
96-
vm.b = { c: 3 }
92+
// unwatch
93+
unwatch()
94+
vm.a = 3
9795
nextTick(function () {
98-
expect(spy).toHaveBeenCalledWith(5, 4)
99-
expect(spy2).toHaveBeenCalledWith(5, 4)
100-
// unwatch
101-
unwatch()
102-
unwatch2()
103-
vm.a = 3
104-
nextTick(function () {
105-
expect(spy.calls.count()).toBe(3)
106-
expect(spy2.calls.count()).toBe(1)
107-
done()
108-
})
96+
expect(spy.calls.count()).toBe(2)
97+
done()
10998
})
11099
})
111100
})

test/unit/specs/api/lifecycle_spec.js

+2-4
Original file line numberDiff line numberDiff line change
@@ -161,8 +161,6 @@ if (_.inBrowser) {
161161
expect(data.__ob__.vms.length).toBe(0)
162162
expect(vm._isDestroyed).toBe(true)
163163
expect(vm._watchers).toBeNull()
164-
expect(vm._userWatchers).toBeNull()
165-
expect(vm._watcherList).toBeNull()
166164
expect(vm.$el).toBeNull()
167165
expect(vm.$parent).toBeNull()
168166
expect(vm.$root).toBeNull()
@@ -236,8 +234,8 @@ if (_.inBrowser) {
236234
data: { a: 1 }
237235
})
238236
vm.$watch('a', function () {})
239-
var dirWatcher = vm._watcherList[0]
240-
var userWatcher = vm._watcherList[1]
237+
var dirWatcher = vm._watchers[0]
238+
var userWatcher = vm._watchers[1]
241239
vm.$destroy()
242240
expect(dirWatcher.active).toBe(false)
243241
expect(userWatcher.active).toBe(false)

test/unit/specs/directive_spec.js

-21
Original file line numberDiff line numberDiff line change
@@ -162,25 +162,4 @@ describe('Directive', function () {
162162
expect(def.update).toHaveBeenCalled()
163163
})
164164

165-
it('reuse the same watcher', function (done) {
166-
var d = new Directive('test', el, vm, {
167-
expression: 'a',
168-
}, def)
169-
var d2 = new Directive('test', el, vm, {
170-
expression: 'a',
171-
}, def)
172-
expect(vm._watcherList.length).toBe(1)
173-
expect(d._watcher).toBe(d2._watcher)
174-
d2._teardown()
175-
expect(d2._watcher).toBeNull()
176-
expect(vm._watcherList.length).toBe(1)
177-
vm.a = 2
178-
nextTick(function () {
179-
expect(def.update).toHaveBeenCalledWith(2, 1)
180-
d._teardown()
181-
expect(vm._watcherList.length).toBe(0)
182-
done()
183-
})
184-
})
185-
186165
})

test/unit/specs/directives/model_spec.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -232,9 +232,9 @@ if (_.inBrowser) {
232232
expect(opts[0].selected).toBe(true)
233233
expect(opts[1].selected).toBe(false)
234234
// should teardown option watcher when unbind
235-
expect(vm._watcherList.length).toBe(2)
235+
expect(vm._watchers.length).toBe(2)
236236
vm._directives[0]._teardown()
237-
expect(vm._watcherList.length).toBe(0)
237+
expect(vm._watchers.length).toBe(0)
238238
done()
239239
})
240240
})

test/unit/specs/instance/init_spec.js

-2
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,7 @@ describe('Instance Init', function () {
2424
expect(stub.$el).toBe(null)
2525
expect(stub.$root).toBe(stub)
2626
expect(stub.$).toBeTruthy()
27-
expect(stub._watcherList).toBeTruthy()
2827
expect(stub._watchers).toBeTruthy()
29-
expect(stub._userWatchers).toBeTruthy()
3028
expect(stub._directives).toBeTruthy()
3129
expect(stub._events).toBeTruthy()
3230
expect(stub._eventsCount).toBeTruthy()

test/unit/specs/watcher_spec.js

+1-49
Original file line numberDiff line numberDiff line change
@@ -315,47 +315,14 @@ describe('Watcher', function () {
315315
})
316316
})
317317

318-
it('add callback', function (done) {
319-
var watcher = new Watcher(vm, 'a', spy)
320-
var spy2 = jasmine.createSpy()
321-
watcher.addCb(spy2)
322-
vm.a = 99
323-
nextTick(function () {
324-
expect(spy).toHaveBeenCalledWith(99, 1)
325-
expect(spy2).toHaveBeenCalledWith(99, 1)
326-
done()
327-
})
328-
})
329-
330-
it('remove callback', function (done) {
331-
// single, should equal teardown
332-
var fn = function () {}
333-
var watcher = new Watcher(vm, 'a', fn)
334-
watcher.removeCb(fn)
335-
expect(watcher.active).toBe(false)
336-
expect(watcher.vm).toBe(null)
337-
expect(watcher.cbs).toBe(null)
338-
// multiple
339-
watcher = new Watcher(vm, 'a', spy)
340-
var spy2 = jasmine.createSpy()
341-
watcher.addCb(spy2)
342-
watcher.removeCb(spy)
343-
vm.a = 234
344-
nextTick(function () {
345-
expect(spy).not.toHaveBeenCalled()
346-
expect(spy2).toHaveBeenCalledWith(234, 1)
347-
done()
348-
})
349-
})
350-
351318
it('teardown', function (done) {
352319
var watcher = new Watcher(vm, 'b.c', spy)
353320
watcher.teardown()
354321
vm.b.c = 3
355322
nextTick(function () {
356323
expect(watcher.active).toBe(false)
357324
expect(watcher.vm).toBe(null)
358-
expect(watcher.cbs).toBe(null)
325+
expect(watcher.cb).toBe(null)
359326
expect(spy).not.toHaveBeenCalled()
360327
done()
361328
})
@@ -372,21 +339,6 @@ describe('Watcher', function () {
372339
config.async = true
373340
})
374341

375-
it('handle a cb that triggers removeCb', function () {
376-
var watcher = new Watcher(vm, 'a', spy)
377-
watcher.addCb(function () {
378-
watcher.removeCb(spy)
379-
})
380-
watcher.addCb(function () {})
381-
config.async = false
382-
expect(function () {
383-
vm.a = 2
384-
}).not.toThrow()
385-
config.async = true
386-
expect(spy).toHaveBeenCalled()
387-
expect(watcher.cbs.length).toBe(2)
388-
})
389-
390342
it('warn getter errors', function () {
391343
var watcher = new Watcher(vm, 'd.e + c', spy)
392344
expect(hasWarned(_, 'Error when evaluating expression')).toBe(true)

0 commit comments

Comments
 (0)