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