Ver código fonte

work on bindings

Evan You 11 anos atrás
pai
commit
67cec80173

+ 6 - 0
changes.md

@@ -9,6 +9,12 @@ var vm = new Vue({ data: {a:1} }) // only observes the data
 vm.$mount('#app') // actually compile the DOM
 ```
 
+## Scope & Data
+
+- new option: `syncData`.
+
+Each Vue instance now creates an associated `$scope` object which has prototypal inheritance similar to Angular. This makes expression evaluation much cleaner. A side effect of this change is that the `data` object being passed in is no longer mutated by default. You need to now explicitly pass in  `syncData: true` in the options for direct property changes on the scope to be synced back to the root data object. In most cases, this is not necessary.
+
 ## More flexible directive syntax
   
   - v-on

+ 35 - 0
src/binding.js

@@ -4,8 +4,43 @@ function Binding () {
 
 var p = Binding.prototype
 
+/**
+ * Add a child binding to the tree.
+ *
+ * @param {String} key
+ * @param {Binding} child
+ */
+
 p.addChild = function (key, child) {
   
 }
 
+/**
+ * Traverse along a path and trigger updates
+ * along the way.
+ *
+ * @param {String} path
+ */
+
+p.updatePath = function (path) {
+  
+}
+
+/**
+ * Trigger updates for the subtree starting at
+ * self as root.
+ */
+
+p.updateSubTree = function () {
+  
+}
+
+/**
+ * Notify all subscribers of it self
+ */
+
+p.notify = function () {
+  
+}
+
 module.exports = Binding

+ 36 - 3
src/instance/bindings.js

@@ -1,7 +1,22 @@
 var Binding = require('../binding')
+var Observer = require('../observe/observer')
+var Path = require('../parse/path')
 
 /**
- * Setup the bindings graph.
+ * Setup the binding tree.
+ *
+ * Bindings form a tree-like structure that maps the Object structure
+ * of observed data. However, only paths present in the templates are
+ * created in the binding tree. When a change event from the data 
+ * observer arrives on the instance, we traverse the binding tree
+ * along the changed path, triggering binding updates along the way.
+ * When we reach the path endpoint, if it has any children, we also
+ * trigger updates on the entire sub-tree.
+ *
+ * Each instance has a root binding and it has three special children:
+ * `$data`, `$parent` & `$root`. `$data` points to the root binding
+ * itself. `$parent` and `$root` point to the instance's parent and
+ * root's root bindings, respectively.
  */
 
 exports._initBindings = function () {
@@ -14,12 +29,30 @@ exports._initBindings = function () {
     root.addChild('$parent', this.$parent._rootBinding)
     root.addChild('$root', this.$root._rootBinding)
   }
+  this._observer
+    .on('set', this._updateBindings)
+    .on('add', this._updateBindings)
+    .on('delete', this._updateBindings)
+    .on('mutate', this._updateBindings)
 }
 
 /**
- * Create a binding
+ * Create bindings along a path
+ *
+ * @param {String|Array} path
  */
 
-exports._createBinding = function (key) {
+exports._createBindings = function (path) {
   
+}
+
+/**
+ * Traverse the binding tree
+ *
+ * @param {String} path
+ */
+
+exports._updateBindings = function (path) {
+  path = path.split(Observer.pathDelimiter)
+  this._rootBinding.updatePath(path)
 }

+ 32 - 18
src/instance/data.js

@@ -1,8 +1,17 @@
 var Observer = require('../observe/observer')
 
 /**
- * Setup the instances data object, copying properties into
- * scope and setup the syncing between the data and the scope.
+ * Setup the instances data object.
+ *
+ * Properties are copied into the scope object to take advantage of
+ * prototypal inheritance.
+ *
+ * If the `syncData` option is true, Vue will maintain property
+ * syncing between the scope and the original data object, so that
+ * any changes to the scope are synced back to the passed in object.
+ * This is useful internally when e.g. creating v-repeat instances
+ * with no alias.
+ *
  * If swapping data object with the `$data` accessor, teardown
  * previous sync listeners and delete keys not present in new data.
  *
@@ -12,11 +21,14 @@ var Observer = require('../observe/observer')
 
 exports._initData = function (data, init) {
   var scope = this.$scope
+  var options = this.$options
   var key
 
   if (!init) {
     // teardown old sync listeners
-    this._unsync()
+    if (options.syncData) {
+      this._unsync()
+    }
     // delete keys not present in the new data
     for (key in scope) {
       if (scope.hasOwnProperty(key) && !(key in data)) {
@@ -37,9 +49,11 @@ exports._initData = function (data, init) {
   }
 
   // setup sync between scope and new data
-  this._data = data
-  this._dataObserver = Observer.create(data)
-  this._sync()
+  if (options.syncData) {
+    this._data = data
+    this._dataObserver = Observer.create(data)
+    this._sync()
+  }
 }
 
 /**
@@ -57,10 +71,10 @@ exports._sync = function () {
       set: guard(function (key, val) {
         data[key] = val
       }),
-      added: guard(function (key, val) {
+      add: guard(function (key, val) {
         data.$add(key, val)
       }),
-      deleted: guard(function (key) {
+      delete: guard(function (key) {
         data.$delete(key)
       })
     },
@@ -68,10 +82,10 @@ exports._sync = function () {
       set: guard(function (key, val) {
         scope[key] = val
       }),
-      added: guard(function (key, val) {
+      add: guard(function (key, val) {
         scope.$add(key, val)
       }),
-      deleted: guard(function (key) {
+      delete: guard(function (key) {
         scope.$delete(key)
       })
     }
@@ -80,13 +94,13 @@ exports._sync = function () {
   // sync scope and original data.
   this._observer
     .on('set:self', listeners.data.set)
-    .on('added:self', listeners.data.added)
-    .on('deleted:self', listeners.data.deleted)
+    .on('add:self', listeners.data.add)
+    .on('delete:self', listeners.data.delete)
 
   this._dataObserver
     .on('set:self', listeners.scope.set)
-    .on('added:self', listeners.scope.added)
-    .on('deleted:self', listeners.scope.deleted)
+    .on('add:self', listeners.scope.add)
+    .on('delete:self', listeners.scope.delete)
 
   /**
    * The guard function prevents infinite loop
@@ -112,11 +126,11 @@ exports._unsync = function () {
 
   this._observer
     .off('set:self', listeners.data.set)
-    .off('added:self', listeners.data.added)
-    .off('deleted:self', listeners.data.deleted)
+    .off('add:self', listeners.data.add)
+    .off('delete:self', listeners.data.delete)
 
   this._dataObserver
     .off('set:self', listeners.scope.set)
-    .off('added:self', listeners.scope.added)
-    .off('deleted:self', listeners.scope.deleted)
+    .off('add:self', listeners.scope.add)
+    .off('delete:self', listeners.scope.delete)
 }

+ 2 - 2
src/instance/proxy.js

@@ -24,10 +24,10 @@ exports._initProxy = function () {
   }
   // keep proxying up-to-date with added/deleted keys.
   this._observer
-    .on('added:self', function (key) {
+    .on('add:self', function (key) {
       _.proxy(this, scope, key)
     })
-    .on('deleted:self', function (key) {
+    .on('delete:self', function (key) {
       delete this[key]
     })
 }

+ 2 - 2
src/instance/scope.js

@@ -1,5 +1,5 @@
 var Observer = require('../observe/observer')
-var scopeEvents = ['set', 'mutate', 'added', 'deleted']
+var scopeEvents = ['set', 'mutate', 'add', 'delete']
 
 /**
  * Setup instance scope.
@@ -13,7 +13,7 @@ var scopeEvents = ['set', 'mutate', 'added', 'deleted']
 exports._initScope = function () {
   var options = this.$options
   var parent = this.$parent = options.parent
-  var scope = this.$scope = parent && options._inheritScope !== false
+  var scope = this.$scope = parent
     ? Object.create(parent.$scope)
     : {}
   // create scope observer

+ 4 - 4
src/observe/object-augmentations.js

@@ -17,8 +17,8 @@ _.define(objectAgumentations, '$add', function (key, val) {
   var ob = this.$observer
   ob.observe(key, val)
   ob.convert(key, val)
-  ob.emit('added:self', key, val)
-  ob.propagate('added', key, val)
+  ob.emit('add:self', key, val)
+  ob.propagate('add', key, val)
 })
 
 /**
@@ -33,8 +33,8 @@ _.define(objectAgumentations, '$delete', function (key) {
   if (!this.hasOwnProperty(key)) return
   delete this[key]
   var ob = this.$observer
-  ob.emit('deleted:self', key)
-  ob.propagate('deleted', key)
+  ob.emit('delete:self', key)
+  ob.propagate('delete', key)
 })
 
 module.exports = objectAgumentations

+ 0 - 0
src/parse/template.js


+ 1 - 1
src/vue.js

@@ -21,7 +21,7 @@ function Vue (options) {
   this._initData(options.data || {}, true)
   // setup property proxying
   this._initProxy()
-  // setup root binding
+  // setup binding tree
   this._initBindings()
 }