save and throw async stack trace in debug mode

This commit is contained in:
Evan You 2015-08-16 18:06:24 -04:00
parent da315f840b
commit b9d8ad37ce
3 changed files with 36 additions and 18 deletions

View File

@ -73,15 +73,12 @@ exports.$delete = function (key) {
exports.$watch = function (exp, cb, options) {
var vm = this
var wrappedCb = function (val, oldVal) {
cb.call(vm, val, oldVal)
}
var watcher = new Watcher(vm, exp, wrappedCb, {
var watcher = new Watcher(vm, exp, cb, {
deep: options && options.deep,
user: !options || options.user !== false
})
if (options && options.immediate) {
wrappedCb(watcher.value)
cb.call(vm, watcher.value)
}
return function unwatchFn () {
watcher.teardown()

View File

@ -25,6 +25,10 @@ var uid = 0
*/
function Watcher (vm, expOrFn, cb, options) {
// mix in options
if (options) {
_.extend(this, options)
}
var isFn = typeof expOrFn === 'function'
this.vm = vm
vm._watchers.push(this)
@ -32,23 +36,16 @@ function Watcher (vm, expOrFn, cb, options) {
this.cb = cb
this.id = ++uid // uid for batching
this.active = true
options = options || {}
this.deep = !!options.deep
this.user = !!options.user
this.twoWay = !!options.twoWay
this.sync = !!options.sync
this.lazy = !!options.lazy
this.dirty = this.lazy
this.filters = options.filters
this.preProcess = options.preProcess
this.dirty = this.lazy // for lazy watchers
this.deps = []
this.newDeps = null
this.prevError = null // for async error stacks
// parse expression for getter/setter
if (isFn) {
this.getter = expOrFn
this.setter = undefined
} else {
var res = expParser.parse(expOrFn, options.twoWay)
var res = expParser.parse(expOrFn, this.twoWay)
this.getter = res.get
this.setter = res.set
}
@ -196,6 +193,11 @@ p.update = function (shallow) {
: false
: !!shallow
this.queued = true
// record before-push error stack in debug mode
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && config.debug) {
this.prevError = new Error('[vue] async stack trace')
}
batcher.push(this)
}
}
@ -216,9 +218,28 @@ p.run = function () {
// non-shallow update (caused by a vm digest).
((_.isArray(value) || this.deep) && !this.shallow)
) {
// set new value
var oldValue = this.value
this.value = value
this.cb(value, oldValue)
// in debug + async mode, when a watcher callbacks
// throws, we also throw the saved before-push error
// so the full cross-tick stack trace is available.
var prevError = this.prevError
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' &&
config.debug && prevError) {
this.prevError = null
try {
this.cb.call(this.vm, value, oldValue)
} catch (e) {
_.nextTick(function () {
throw prevError
}, 0)
throw e
}
} else {
this.cb.call(this.vm, value, oldValue)
}
}
this.queued = this.shallow = false
}

View File

@ -92,7 +92,7 @@ describe('Data API', function () {
var unwatch = vm.$watch('a + b.c', spy, {
immediate: true
})
expect(spy).toHaveBeenCalledWith(3, undefined)
expect(spy).toHaveBeenCalledWith(3)
vm.a = 2
nextTick(function () {
expect(spy).toHaveBeenCalledWith(4, 3)
@ -112,7 +112,7 @@ describe('Data API', function () {
var unwatch = vm.$watch(function () {
return this.a + this.b.c
}, spy, { immediate: true })
expect(spy).toHaveBeenCalledWith(3, undefined)
expect(spy).toHaveBeenCalledWith(3)
vm.a = 2
nextTick(function () {
expect(spy).toHaveBeenCalledWith(4, 3)