Sfoglia il codice sorgente

fix observer binding ownship model

Evan You 11 anni fa
parent
commit
0c1e4fce2f
4 ha cambiato i file con 55 aggiunte e 29 eliminazioni
  1. 1 1
      src/observer/array.js
  2. 32 15
      src/observer/index.js
  3. 2 2
      src/observer/object.js
  4. 20 11
      test/unit/specs/observer_spec.js

+ 1 - 1
src/observer/array.js

@@ -41,7 +41,7 @@ var arrayAugmentations = Object.create(Array.prototype)
     }
     if (inserted) ob.observeArray(inserted)
     // notify change
-    ob.binding.notify()
+    ob.notify()
     return result
   })
 })

+ 32 - 15
src/observer/index.js

@@ -59,7 +59,7 @@ function Observer (value, type) {
   this.id = ++uid
   this.value = value
   this.active = true
-  this.binding = new Binding()
+  this.bindings = []
   _.define(value, '__ob__', this)
   var augment = config.proto && _.hasProto
     ? protoAugment
@@ -135,8 +135,7 @@ p.walk = function (obj) {
  */
 
 p.observe = function (val) {
-  var ob = Observer.create(val)
-  if (ob) return ob.binding
+  return Observer.create(val)
 }
 
 /**
@@ -162,7 +161,11 @@ p.observeArray = function (items) {
 
 p.convert = function (key, val) {
   var ob = this
-  var binding = ob.observe(val) || new Binding()
+  var childOb = ob.observe(val)
+  var binding = new Binding()
+  if (childOb) {
+    childOb.bindings.push(binding)
+  }
   Object.defineProperty(ob.value, key, {
     enumerable: true,
     configurable: true,
@@ -176,21 +179,35 @@ p.convert = function (key, val) {
     },
     set: function (newVal) {
       if (newVal === val) return
+      // remove binding from old value
+      var oldChildOb = val && val.__ob__
+      if (oldChildOb) {
+        var oldBindings = oldChildOb.bindings
+        oldBindings.splice(oldBindings.indexOf(binding))
+      }
       val = newVal
-      var newBinding = ob.observe(newVal)
-      if (newBinding) {
-        // swap binding, then call notify on old binding.
-        // this ensures all subscribers of the old binding
-        // gets re-evaluated, picks up the new binding and
-        // unregister from old binding.
-        var oldBinding = binding
-        binding = newBinding
-        oldBinding.notify()
-      } else {
-        binding.notify()
+      // add binding to new value
+      var newChildOb = ob.observe(newVal)
+      if (newChildOb) {
+        newChildOb.bindings.push(binding)
       }
+      binding.notify()
     }
   })
 }
 
+/**
+ * Notify change on all self bindings on an observer.
+ * This is called when a mutable value mutates. e.g.
+ * when an Array's mutating methods are called, or an
+ * Object's $add/$delete are called.
+ */
+
+p.notify = function () {
+  var bindings = this.bindings
+  for (var i = 0, l = bindings.length; i < l; i++) {
+    bindings[i].notify()
+  }
+}
+
 module.exports = Observer

+ 2 - 2
src/observer/object.js

@@ -16,7 +16,7 @@ _.define(
   function $add (key, val) {
     if (this.hasOwnProperty(key)) return
     this.__ob__.convert(key, val)
-    this.__ob__.binding.notify()
+    this.__ob__.notify()
   }
 )
 
@@ -34,7 +34,7 @@ _.define(
   function $delete (key) {
     if (!this.hasOwnProperty(key)) return
     delete this[key]
-    this.__ob__.binding.notify()
+    this.__ob__.notify()
   }
 )
 

+ 20 - 11
test/unit/specs/observer_spec.js

@@ -1,5 +1,6 @@
 var Observer = require('../../../src/observer')
 var config = require('../../../src/config')
+var Binding = require('../../../src/binding')
 var _ = require('../../../src/util')
 
 describe('Observer', function () {
@@ -71,8 +72,11 @@ describe('Observer', function () {
     dump = obj.a.b = 3
     expect(watcher.update.calls.count()).toBe(1)
     // swap object
+    var oldA = obj.a
     obj.a = { b: 4 }
     expect(watcher.update.calls.count()).toBe(2)
+    expect(oldA.__ob__.bindings.length).toBe(0)
+    expect(obj.a.__ob__.bindings.length).toBe(1)
     // recollect dep
     var oldDeps = watcher.deps
     watcher.deps = []
@@ -80,9 +84,6 @@ describe('Observer', function () {
     dump = obj.a.b
     Observer.target = null
     expect(watcher.deps.length).toBe(2)
-    // make sure we picked up the new bindings
-    expect(watcher.deps[0]).not.toBe(oldDeps[0])
-    expect(watcher.deps[1]).not.toBe(oldDeps[1])
     // set on the swapped object
     obj.a.b = 5
     expect(watcher.update.calls.count()).toBe(3)
@@ -91,7 +92,8 @@ describe('Observer', function () {
   it('observing $add/$delete', function () {
     var obj = { a: 1 }
     var ob = Observer.create(obj)
-    var binding = ob.binding
+    var binding = new Binding()
+    ob.bindings.push(binding)
     spyOn(binding, 'notify')
     obj.$add('b', 2)
     expect(obj.b).toBe(2)
@@ -111,7 +113,8 @@ describe('Observer', function () {
   it('observing array mutation', function () {
     var arr = []
     var ob = Observer.create(arr)
-    var binding = ob.binding
+    var binding = new Binding()
+    ob.bindings.push(binding)
     spyOn(binding, 'notify')
     var objs = [{}, {}, {}]
     arr.push(objs[0])
@@ -131,7 +134,8 @@ describe('Observer', function () {
   it('array $set', function () {
     var arr = [1]
     var ob = Observer.create(arr)
-    var binding = ob.binding
+    var binding = new Binding()
+    ob.bindings.push(binding)
     spyOn(binding, 'notify')
     arr.$set(0, 2)
     expect(arr[0]).toBe(2)
@@ -147,7 +151,8 @@ describe('Observer', function () {
     var obj1 = arr[0]
     var obj2 = arr[1]
     var ob = Observer.create(arr)
-    var binding = ob.binding
+    var binding = new Binding()
+    ob.bindings.push(binding)
     spyOn(binding, 'notify')
     // remove by index
     arr.$remove(0)
@@ -172,18 +177,22 @@ describe('Observer', function () {
     var ob = Observer.create(obj)
     expect(obj.$add).toBeTruthy()
     expect(obj.$delete).toBeTruthy()
-    spyOn(ob.binding, 'notify')
+    var binding = new Binding()
+    ob.bindings.push(binding)
+    spyOn(binding, 'notify')
     obj.$add('b', 2)
-    expect(ob.binding.notify).toHaveBeenCalled()
+    expect(binding.notify).toHaveBeenCalled()
     // array
     var arr = [1, 2, 3]
     var ob2 = Observer.create(arr)
     expect(arr.$set).toBeTruthy()
     expect(arr.$remove).toBeTruthy()
     expect(arr.push).not.toBe([].push)
-    spyOn(ob2.binding, 'notify')
+    var binding2 = new Binding()
+    ob2.bindings.push(binding2)
+    spyOn(binding2, 'notify')
     arr.push(1)
-    expect(ob2.binding.notify).toHaveBeenCalled()
+    expect(binding2.notify).toHaveBeenCalled()
     config.proto = true
   })