mirror of https://github.com/vuejs/vue.git
further simplify observe
This commit is contained in:
parent
1dfb10e491
commit
36599833de
|
|
@ -46,6 +46,7 @@ exports.$add = function (key, val) {
|
|||
if (!_.isReserved(key)) {
|
||||
this._data.$add(key, val)
|
||||
this._proxy(key)
|
||||
this._digest()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -59,6 +60,7 @@ exports.$delete = function (key) {
|
|||
if (!_.isReserved(key)) {
|
||||
this._data.$delete(key)
|
||||
this._unproxy(key)
|
||||
this._digest()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -115,8 +115,6 @@ exports.$destroy = function (remove) {
|
|||
for (i in this._userWatchers) {
|
||||
this._userWatchers[i].teardown()
|
||||
}
|
||||
// teardown data/scope
|
||||
this._teardownScope()
|
||||
// clean up
|
||||
this._data =
|
||||
this._watchers =
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ exports._init = function (options) {
|
|||
this.$el = null
|
||||
this.$root = this.$root || this
|
||||
this.$ = {}
|
||||
this._watcherList = []
|
||||
this._watchers = {}
|
||||
this._userWatchers = {}
|
||||
this._directives = []
|
||||
|
|
|
|||
|
|
@ -23,18 +23,6 @@ exports._initScope = function () {
|
|||
this._initMeta()
|
||||
}
|
||||
|
||||
/**
|
||||
* Teardown the scope.
|
||||
*/
|
||||
|
||||
exports._teardownScope = function () {
|
||||
var dataOb = this._data.__ob__
|
||||
dataOb.vmCount--
|
||||
dataOb.tryRelease()
|
||||
// unset data reference
|
||||
this._data = null
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the data.
|
||||
*/
|
||||
|
|
@ -48,8 +36,7 @@ exports._initData = function () {
|
|||
this._proxy(keys[i])
|
||||
}
|
||||
// observe data
|
||||
var ob = Observer.create(data)
|
||||
ob.vmCount++
|
||||
Observer.create(data)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -82,20 +69,8 @@ exports._setData = function (newData) {
|
|||
this._proxy(key)
|
||||
}
|
||||
}
|
||||
// observe new / teardown old
|
||||
var newOb = Observer.create(newData)
|
||||
var oldOb = oldData.__ob__
|
||||
newOb.vmCount++
|
||||
oldOb.vmCount--
|
||||
// memory managment, important!
|
||||
oldOb.tryRelease()
|
||||
// update ALL watchers
|
||||
for (key in this._watchers) {
|
||||
this._watchers[key].update()
|
||||
}
|
||||
for (key in this._userWatchers) {
|
||||
this._userWatchers[key].update()
|
||||
}
|
||||
Observer.create(newData)
|
||||
this._digest()
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -134,6 +109,23 @@ exports._unproxy = function (key) {
|
|||
delete this[key]
|
||||
}
|
||||
|
||||
/**
|
||||
* Force update on every watcher in scope.
|
||||
*/
|
||||
|
||||
exports._digest = function () {
|
||||
var i = this._watcherList.length
|
||||
while (i--) {
|
||||
this._watcherList[i].update()
|
||||
}
|
||||
if (this._children) {
|
||||
i = this._children.length
|
||||
while (i--) {
|
||||
this._children[i]._digest()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup computed properties. They are essentially
|
||||
* special getter/setters
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@ var arrayAugmentations = Object.create(Array.prototype)
|
|||
}
|
||||
var result = original.apply(this, args)
|
||||
var ob = this.__ob__
|
||||
var inserted, removed
|
||||
|
||||
var inserted
|
||||
switch (method) {
|
||||
case 'push':
|
||||
inserted = args
|
||||
|
|
@ -37,28 +36,13 @@ var arrayAugmentations = Object.create(Array.prototype)
|
|||
case 'unshift':
|
||||
inserted = args
|
||||
break
|
||||
case 'pop':
|
||||
removed = [result]
|
||||
break
|
||||
case 'shift':
|
||||
removed = [result]
|
||||
break
|
||||
case 'splice':
|
||||
inserted = args.slice(2)
|
||||
removed = result
|
||||
break
|
||||
}
|
||||
|
||||
// link/unlink added/removed elements
|
||||
if (inserted) ob.observeArray(inserted)
|
||||
if (removed) ob.unobserveArray(removed)
|
||||
|
||||
// notify bindings
|
||||
i = ob.bindings.length
|
||||
while (i--) {
|
||||
ob.bindings[i].notify()
|
||||
}
|
||||
|
||||
// notify change
|
||||
ob.binding.notify()
|
||||
return result
|
||||
}
|
||||
// define wrapped method
|
||||
|
|
|
|||
|
|
@ -27,13 +27,12 @@ function Observer (value, type) {
|
|||
this.id = ++uid
|
||||
this.value = value
|
||||
this.type = type
|
||||
this.parentCount = 0
|
||||
this.vmCount = 0
|
||||
this.active = true
|
||||
this.binding = new Binding()
|
||||
if (value) {
|
||||
_.define(value, '__ob__', this)
|
||||
if (type === ARRAY) {
|
||||
_.augment(value, arrayAugmentations)
|
||||
this.bindings = []
|
||||
this.observeArray(value)
|
||||
} else if (type === OBJECT) {
|
||||
_.augment(value, objectAugmentations)
|
||||
|
|
@ -100,35 +99,13 @@ p.walk = function (obj) {
|
|||
* and if value is array, link binding to the array.
|
||||
*
|
||||
* @param {*} val
|
||||
* @param {Binding} [binding]
|
||||
*/
|
||||
|
||||
p.observe = function (val, binding) {
|
||||
p.observe = function (val) {
|
||||
var ob = Observer.create(val)
|
||||
if (ob) {
|
||||
ob.parentCount++
|
||||
if (binding && ob.type === ARRAY) {
|
||||
ob.bindings.push(binding)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unobserve a value.
|
||||
*
|
||||
* @param {*} val
|
||||
* @param {Binding} [binding]
|
||||
*/
|
||||
|
||||
p.unobserve = function (val, binding) {
|
||||
var ob = val && val.__ob__
|
||||
if (ob) {
|
||||
ob.parentCount--
|
||||
if (binding && ob.type === ARRAY) {
|
||||
var i = ob.bindings.indexOf(binding)
|
||||
if (i > -1) ob.bindings.splice()
|
||||
}
|
||||
ob.tryRelease()
|
||||
// ob.parentCount++
|
||||
return ob.binding
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -145,19 +122,6 @@ p.observeArray = function (items) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unobserve a list of Array items.
|
||||
*
|
||||
* @param {Array} items
|
||||
*/
|
||||
|
||||
p.unobserveArray = function (items) {
|
||||
var i = items.length
|
||||
while (i--) {
|
||||
this.unobserve(items[i])
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a property into getter/setter so we can emit
|
||||
* the events when the property is accessed/changed.
|
||||
|
|
@ -168,58 +132,31 @@ p.unobserveArray = function (items) {
|
|||
|
||||
p.convert = function (key, val) {
|
||||
var ob = this
|
||||
var binding = new Binding()
|
||||
ob.observe(val, binding)
|
||||
var binding = ob.observe(val) || new Binding()
|
||||
Object.defineProperty(ob.value, key, {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
get: function () {
|
||||
// Observer.target is a watcher whose getter is
|
||||
// currently being evaluated.
|
||||
if (Observer.target) {
|
||||
if (ob.active && Observer.target) {
|
||||
Observer.target.addDep(binding)
|
||||
}
|
||||
return val
|
||||
},
|
||||
set: function (newVal) {
|
||||
if (newVal === val) return
|
||||
ob.unobserve(val, binding)
|
||||
ob.observe(newVal, binding)
|
||||
val = newVal
|
||||
var newBinding = ob.observe(newVal)
|
||||
if (newBinding) {
|
||||
// handle over binding
|
||||
newBinding.subs = binding.subs
|
||||
binding.subs = []
|
||||
binding = newBinding
|
||||
}
|
||||
binding.notify()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to teardown the observer if the value is no
|
||||
* longer needed. Two requirements have to be met:
|
||||
*
|
||||
* 1. The observer has no parent obervers depending on it.
|
||||
* 2. The observer is not being used as the root $data by
|
||||
* by a vm instance.
|
||||
*
|
||||
* This is important because each observer holds strong
|
||||
* reference to all its parents and if we don't do this
|
||||
* those parents can be leaked when a vm is destroyed.
|
||||
*/
|
||||
|
||||
p.tryRelease = function () {
|
||||
if (!this.parentCount && !this.vmCount) {
|
||||
var value = this.value
|
||||
if (_.isArray(value)) {
|
||||
value.__ob__.bindings = null
|
||||
this.unobserveArray(value)
|
||||
} else {
|
||||
for (var key in value) {
|
||||
var val = value[key]
|
||||
this.unobserve(val)
|
||||
// release closure
|
||||
_.define(value, key, val, true)
|
||||
}
|
||||
}
|
||||
value.__ob__ = null
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Observer
|
||||
|
|
@ -13,6 +13,7 @@ var objectAgumentations = Object.create(Object.prototype)
|
|||
function $add (key, val) {
|
||||
if (this.hasOwnProperty(key)) return
|
||||
this.__ob__.convert(key, val)
|
||||
this.__ob__.binding.notify()
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -25,8 +26,8 @@ function $add (key, val) {
|
|||
|
||||
function $delete (key) {
|
||||
if (!this.hasOwnProperty(key)) return
|
||||
this.__ob__.unobserve(this[key])
|
||||
delete this[key]
|
||||
this.__ob__.binding.notify()
|
||||
}
|
||||
|
||||
if (_.hasProto) {
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ var uid = 0
|
|||
|
||||
function Watcher (vm, expression, cb, filters, needSet) {
|
||||
this.vm = vm
|
||||
vm._watcherList.push(this)
|
||||
this.expression = expression
|
||||
this.cbs = [cb]
|
||||
this.id = ++uid // uid for batching
|
||||
|
|
@ -173,6 +174,8 @@ p.removeCb = function (cb) {
|
|||
|
||||
p.teardown = function () {
|
||||
if (this.active) {
|
||||
var list = this.vm._watcherList
|
||||
list.splice(list.indexOf(this))
|
||||
for (var id in this.deps) {
|
||||
this.deps[id].removeSub(this)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -167,16 +167,12 @@ describe('Watcher', function () {
|
|||
var oldData = vm.$data
|
||||
var watcher = new Watcher(vm, '$data', spy)
|
||||
expect(watcher.value).toBe(oldData)
|
||||
vm.a = 2
|
||||
nextTick(function () {
|
||||
expect(spy).toHaveBeenCalledWith(oldData, oldData)
|
||||
var newData = {}
|
||||
vm.$data = newData
|
||||
nextTick(function() {
|
||||
expect(spy).toHaveBeenCalledWith(newData, oldData)
|
||||
expect(watcher.value).toBe(newData)
|
||||
done()
|
||||
})
|
||||
var newData = {}
|
||||
vm.$data = newData
|
||||
nextTick(function() {
|
||||
expect(spy).toHaveBeenCalledWith(newData, oldData)
|
||||
expect(watcher.value).toBe(newData)
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue