Explorar o código

observer rewrite

Evan You %!s(int64=12) %!d(string=hai) anos
pai
achega
216e398b22
Modificáronse 2 ficheiros con 98 adicións e 111 borrados
  1. 97 99
      src/observer.js
  2. 1 12
      test/unit/specs/observer.js

+ 97 - 99
src/observer.js

@@ -86,18 +86,6 @@ for (var method in extensions) {
     def(ArrayProxy, method, extensions[method], !hasProto)
 }
 
-/**
- *  Watch an object based on type
- */
-function watch (obj, path, observer) {
-    var type = typeOf(obj)
-    if (type === 'Object') {
-        watchObject(obj, path, observer)
-    } else if (type === 'Array') {
-        watchArray(obj, path, observer)
-    }
-}
-
 /**
  *  Watch an Object, recursive.
  */
@@ -105,7 +93,7 @@ function watchObject (obj, path, observer) {
     for (var key in obj) {
         var keyPrefix = key.charAt(0)
         if (keyPrefix !== '$' && keyPrefix !== '_') {
-            bind(obj, key, path, observer)
+            convert(obj, key, observer)
         }
     }
 }
@@ -131,33 +119,34 @@ function watchArray (arr, path, observer) {
  *  so it emits get/set events.
  *  Then watch the value itself.
  */
-function bind (obj, key, path, observer) {
+function convert (obj, key, observer) {
     var val       = obj[key],
-        watchable = isWatchable(val),
-        values    = observer.values,
-        fullKey   = (path ? path + '.' : '') + key
-    values[fullKey] = val
+        values    = observer.values
+    values[key] = val
     // emit set on bind
     // this means when an object is observed it will emit
     // a first batch of set events.
-    observer.emit('set', fullKey, val)
+    observer.emit('set', key, val)
     Object.defineProperty(obj, key, {
         enumerable: true,
         get: function () {
+            var value = values[key]
             // only emit get on tip values
-            if (depsOb.active && !watchable) {
-                observer.emit('get', fullKey)
+            if (depsOb.active && !isWatchable(value)) {
+                observer.emit('get', key)
             }
-            return values[fullKey]
+            return value
         },
         set: function (newVal) {
-            values[fullKey] = newVal
-            ensurePaths(key, newVal, values)
-            observer.emit('set', fullKey, newVal)
-            watch(newVal, fullKey, observer)
+            var oldVal = values[key]
+            unobserve(oldVal, key, observer)
+            values[key] = newVal
+            ensurePaths('', newVal, oldVal)
+            observer.emit('set', key, newVal)
+            observe(newVal, key, observer)
         }
     })
-    watch(val, fullKey, observer)
+    observe(val, key, observer)
 }
 
 /**
@@ -175,14 +164,17 @@ function isWatchable (obj) {
  *  the watch conversion and simply emit set event for
  *  all of its properties.
  */
-function emitSet (obj, observer, set) {
-    if (typeOf(obj) === 'Array') {
-        set('length', obj.length)
-    } else {
-        var key, val, values = observer.values
-        for (key in observer.values) {
-            val = values[key]
-            set(key, val)
+function emitSet (obj) {
+    var type = typeOf(obj),
+        emitter = obj.__observer__
+    if (type === 'Array') {
+        emitter.emit('set', 'length', obj.length)
+    } else if (type === 'Object') {
+        var key, val
+        for (key in obj) {
+            val = obj[key]
+            emitter.emit('set', key, val)
+            emitSet(val)
         }
     }
 }
@@ -194,10 +186,10 @@ function emitSet (obj, observer, set) {
  *  any given path.
  */
 function ensurePaths (key, val, paths) {
-    key += '.'
+    key = key ? key + '.' : ''
     for (var path in paths) {
-        if (!path.indexOf(key)) {
-            ensurePath(val, path.replace(key, ''))
+        if (!key || !path.indexOf(key)) {
+            ensurePath(val, key ? path.replace(key, '') : path)
         }
     }
 }
@@ -222,68 +214,74 @@ function ensurePath (obj, key) {
     return obj[sec]
 }
 
-module.exports = {
-
-    // used in v-repeat
-    watchArray: watchArray,
-    ensurePath: ensurePath,
-    ensurePaths: ensurePaths,
-
-    /**
-     *  Observe an object with a given path,
-     *  and proxy get/set/mutate events to the provided observer.
-     */
-    observe: function (obj, rawPath, observer) {
-        if (isWatchable(obj)) {
-            var path = rawPath + '.',
-                ob, alreadyConverted = !!obj.__observer__
-            if (!alreadyConverted) {
-                def(obj, '__observer__', new Emitter())
-            }
-            ob = obj.__observer__
-            ob.values = ob.values || utils.hash()
-            var proxies = observer.proxies[path] = {
-                get: function (key) {
-                    observer.emit('get', path + key)
-                },
-                set: function (key, val) {
-                    observer.emit('set', path + key, val)
-                },
-                mutate: function (key, val, mutation) {
-                    // if the Array is a root value
-                    // the key will be null
-                    var fixedPath = key ? path + key : rawPath
-                    observer.emit('mutate', fixedPath, val, mutation)
-                    // also emit set for Array's length when it mutates
-                    var m = mutation.method
-                    if (m !== 'sort' && m !== 'reverse') {
-                        observer.emit('set', fixedPath + '.length', val.length)
-                    }
-                }
-            }
-            ob
-                .on('get', proxies.get)
-                .on('set', proxies.set)
-                .on('mutate', proxies.mutate)
-            if (alreadyConverted) {
-                emitSet(obj, ob, proxies.set)
-            } else {
-                watch(obj, null, ob)
+/**
+ *  Observe an object with a given path,
+ *  and proxy get/set/mutate events to the provided observer.
+ */
+function observe (obj, rawPath, observer) {
+    if (!isWatchable(obj)) return
+    var path = rawPath ? rawPath + '.' : '',
+        ob, alreadyConverted = !!obj.__observer__
+    if (!alreadyConverted) {
+        def(obj, '__observer__', new Emitter())
+    }
+    ob = obj.__observer__
+    ob.values = ob.values || utils.hash()
+    observer.proxies = observer.proxies || {}
+    var proxies = observer.proxies[path] = {
+        get: function (key) {
+            observer.emit('get', path + key)
+        },
+        set: function (key, val) {
+            observer.emit('set', path + key, val)
+        },
+        mutate: function (key, val, mutation) {
+            // if the Array is a root value
+            // the key will be null
+            var fixedPath = key ? path + key : rawPath
+            observer.emit('mutate', fixedPath, val, mutation)
+            // also emit set for Array's length when it mutates
+            var m = mutation.method
+            if (m !== 'sort' && m !== 'reverse') {
+                observer.emit('set', fixedPath + '.length', val.length)
             }
         }
-    },
-
-    /**
-     *  Cancel observation, turn off the listeners.
-     */
-    unobserve: function (obj, path, observer) {
-        if (!obj || !obj.__observer__) return
-        path = path + '.'
-        var proxies = observer.proxies[path]
-        obj.__observer__
-            .off('get', proxies.get)
-            .off('set', proxies.set)
-            .off('mutate', proxies.mutate)
-        observer.proxies[path] = null
     }
+    ob
+        .on('get', proxies.get)
+        .on('set', proxies.set)
+        .on('mutate', proxies.mutate)
+    if (alreadyConverted) {
+        emitSet(obj)
+    } else {
+        var type = typeOf(obj)
+        if (type === 'Object') {
+            watchObject(obj, null, ob)
+        } else if (type === 'Array') {
+            watchArray(obj, null, ob)
+        }
+    }
+}
+
+/**
+ *  Cancel observation, turn off the listeners.
+ */
+function unobserve (obj, path, observer) {
+    if (!obj || !obj.__observer__) return
+    path = path + '.'
+    var proxies = observer.proxies[path]
+    obj.__observer__
+        .off('get', proxies.get)
+        .off('set', proxies.set)
+        .off('mutate', proxies.mutate)
+    observer.proxies[path] = null
+}
+
+module.exports = {
+    observe     : observe,
+    unobserve   : unobserve,
+    ensurePath  : ensurePath,
+    ensurePaths : ensurePaths,
+    // used in v-repeat
+    watchArray  : watchArray,
 }

+ 1 - 12
test/unit/specs/observer.js

@@ -8,14 +8,12 @@ describe('UNIT: Observer', function () {
 
         it('should not watch a ViewModel instance', function () {
             var obj = new Vue(), ob = new Emitter()
-            ob.proxies = {}
             Observer.observe(obj, 'test', ob)
             assert.notOk(obj.__observer__)
         })
         
         it('should attach hidden observer and values to the object', function () {
             var obj = {}, ob = new Emitter()
-            ob.proxies = {}
             Observer.observe(obj, 'test', ob)
             assert.ok(obj.__observer__ instanceof Emitter)
             assert.ok(obj.__observer__.values)
@@ -55,7 +53,6 @@ describe('UNIT: Observer', function () {
         it('should emit set when first observing', function () {
             var obj = { a: 1, b: { c: 2} },
                 ob = new Emitter(), i = 0
-            ob.proxies = {}
             var expects = [
                 { key: 'test.a', val: obj.a },
                 { key: 'test.b', val: obj.b },
@@ -76,8 +73,6 @@ describe('UNIT: Observer', function () {
                 ob1 = new Emitter(),
                 ob2 = new Emitter(),
                 i = 0
-            ob1.proxies = {}
-            ob2.proxies = {}
             Observer.observe(obj, 'test', ob1) // watch first time
 
             var expects = [
@@ -86,6 +81,7 @@ describe('UNIT: Observer', function () {
                 { key: 'test.b.c', val: obj.b.c }
             ]
             ob2.on('set', function (key, val) {
+                console.log(key)
                 var exp = expects[i]
                 assert.strictEqual(key, exp.key)
                 assert.strictEqual(val, exp.val)
@@ -101,7 +97,6 @@ describe('UNIT: Observer', function () {
 
         var arr = [],
             ob = new Emitter()
-        ob.proxies = {}
         Observer.observe(arr, 'test', ob)
         
         it('should attach the hidden observer', function () {
@@ -371,8 +366,6 @@ describe('UNIT: Observer', function () {
         var ob1 = new Emitter(),
             ob2 = new Emitter(),
             obj = {a:1}
-        ob1.proxies = {}
-        ob2.proxies = {}
         Observer.observe(obj, 'test', ob1)
         Observer.observe(obj, 'test', ob2)
 
@@ -399,8 +392,6 @@ describe('UNIT: Observer', function () {
         var ob1 = new Emitter(),
             ob2 = new Emitter(),
             obj = {a:1}
-        ob1.proxies = {}
-        ob2.proxies = {}
         Observer.observe(obj, 'test', ob1)
         Observer.observe(obj, 'test', ob2)
         Observer.unobserve(obj, 'test', ob1)
@@ -470,7 +461,6 @@ describe('UNIT: Observer', function () {
                 i  = 0,
                 obj = opts.obj,
                 expects = opts.expects
-            ob.proxies = {}
             Observer.observe(obj, opts.path, ob)
             ob.on('set', function (key, val) {
                 var expect = expects[i]
@@ -499,7 +489,6 @@ describe('UNIT: Observer', function () {
                 i  = 0,
                 obj = opts.obj,
                 expects = opts.expects
-            ob.proxies = {}
             Observer.observe(obj, opts.path, ob)
             ob.on('get', function (key) {
                 var expected = expects[i]