Sfoglia il codice sorgente

observer array index tracking

Evan You 12 anni fa
parent
commit
8bcc3834a9
2 ha cambiato i file con 85 aggiunte e 20 eliminazioni
  1. 20 3
      src/observer/array-augmentations.js
  2. 65 17
      src/observer/observer.js

+ 20 - 3
src/observer/array-augmentations.js

@@ -24,27 +24,41 @@ var arrayAugmentations = Object.create(Array.prototype)
     var args = slice.call(arguments)
     var result = original.apply(this, args)
     var ob = this.$observer
-    var inserted, removed
+    var inserted, removed, index
 
     switch (method) {
       case 'push':
+        inserted = args
+        index = this.length - args.length
+        break
       case 'unshift':
         inserted = args
+        index = 0
         break
       case 'pop':
+        removed = [result]
+        index = this.length
+        break
       case 'shift':
         removed = [result]
+        index = 0
         break
       case 'splice':
         inserted = args.slice(2)
         removed = result
+        index = args[0]
         break
     }
 
     // link/unlink added/removed elements
-    if (inserted) ob.link(inserted)
+    if (inserted) ob.link(inserted, index)
     if (removed) ob.unlink(removed)
 
+    // update indices
+    if (method !== 'push' && method !== 'pop') {
+      ob.updateIndices()
+    }
+
     // emit length change
     if (inserted || removed) {
       ob.notify('set', 'length', this.length)
@@ -55,6 +69,7 @@ var arrayAugmentations = Object.create(Array.prototype)
       method   : method,
       args     : args,
       result   : result,
+      index    : index,
       inserted : inserted,
       removed  : removed
     })
@@ -88,4 +103,6 @@ _.define(arrayAugmentations, '$remove', function (index) {
   if (index > -1) {
     return this.splice(index, 1)[0]
   }
-})
+})
+
+module.exports = arrayAugmentations

+ 65 - 17
src/observer/observer.js

@@ -53,7 +53,14 @@ var p = Observer.prototype = Object.create(Emitter.prototype)
  * which is much less likely to appear in property keys.
  */
 
-var delimiter = Observer.pathDelimiter = '\b'
+Observer.pathDelimiter = '\b'
+
+/**
+ * Switch to globally control whether to emit get events.
+ * Only enabled during dependency collections.
+ */
+
+Observer.emitGet = false
 
 /**
  * Attempt to create an observer instance for a value,
@@ -99,9 +106,10 @@ p.walk = function (obj) {
  * @param {Array} items
  */
 
-p.link = function (items) {
+p.link = function (items, index) {
+  index = index || 0
   for (var i = 0, l = items.length; i < l; i++) {
-    this.observe(i, items[i])
+    this.observe(i + index, items[i])
   }
 }
 
@@ -130,6 +138,7 @@ p.observe = function (key, val) {
   var ob = Observer.create(val)
   if (ob) {
     // register self as a parent of the child observer.
+    if (ob.findParent(this) > -1) return
     (ob.parents || (ob.parents = [])).push({
       ob: this,
       key: key
@@ -146,17 +155,7 @@ p.observe = function (key, val) {
 
 p.unobserve = function (val) {
   if (val && val.$observer) {
-    var parents = val.$observer.parents
-    var i = parents.length
-    while (i--) {
-      if (parents[i].ob === this) {
-        parents.splice(i, 1)
-        break
-      }
-    }
-    if (!parents.length) {
-      val.$observer.parents = null
-    }
+    val.$observer.findParent(this, true)
   }
 }
 
@@ -179,7 +178,9 @@ p.convert = function (key, val) {
     enumerable: true,
     configurable: true,
     get: function () {
-      ob.notify('get', key)
+      if (Observer.emitGet) {
+        ob.notify('get', key)
+      }
       return val
     },
     set: function (newVal) {
@@ -188,7 +189,9 @@ p.convert = function (key, val) {
       ob.observe(key, newVal)
       ob.notify('set', key, newVal)
       if (_.isArray(newVal)) {
-        ob.notify('set', key + delimiter + 'length', newVal.length)
+        ob.notify('set',
+                  key + Observer.pathDelimiter + 'length',
+                  newVal.length)
       }
       val = newVal
     }
@@ -212,10 +215,55 @@ p.notify = function (event, path, val, mutation) {
     var ob = parent.ob
     var key = parent.key
     var parentPath = path
-      ? key + delimiter + path
+      ? key + Observer.pathDelimiter + path
       : key
     ob.notify(event, parentPath, val, mutation)
   }
 }
 
+/**
+ * Update child elements' parent key,
+ * should only be called when value type is Array.
+ */
+
+p.updateIndices = function () {
+  var arr = this.value
+  var i = arr.length
+  var ob
+  while (i--) {
+    ob = arr[i] && arr[i].$observer
+    if (ob) {
+      var j = ob.findParent(this)
+      ob.parents[j].key = i
+    }
+  }
+}
+
+/**
+ * Find a parent option object
+ *
+ * @param {Observer} parent
+ * @param {Boolean} [remove] - whether to remove the parent
+ * @return {Number} - index of parent
+ */
+
+p.findParent = function (parent, remove) {
+  var parents = this.parents
+  if (!parents) return -1
+  var i = parents.length
+  while (i--) {
+    var p = parents[i]
+    if (p.ob === parent) {
+      if (remove) {
+        parents.splice(i, 1)
+        if (!parents.length) {
+          this.parents = null
+        }
+      }
+      return i
+    }
+  }
+  return -1
+}
+
 module.exports = Observer