Jelajahi Sumber

Release-v0.8.6

Evan You 12 tahun lalu
induk
melakukan
eaa6b91f14

+ 1 - 1
bower.json

@@ -1,6 +1,6 @@
 {
     "name": "vue",
-    "version": "0.8.5",
+    "version": "0.8.6",
     "main": "dist/vue.js",
     "description": "Simple, Fast & Composable MVVM for building interative interfaces",
     "authors": ["Evan You <yyx990803@gmail.com>"],

+ 1 - 1
component.json

@@ -1,6 +1,6 @@
 {
     "name": "vue",
-    "version": "0.8.5",
+    "version": "0.8.6",
     "main": "src/main.js",
     "author": "Evan You <yyx990803@gmail.com>",
     "description": "Simple, Fast & Composable MVVM for building interative interfaces",

+ 146 - 92
dist/vue.js

@@ -1,5 +1,5 @@
 /*
- Vue.js v0.8.5
+ Vue.js v0.8.6
  (c) 2014 Evan You
  License: MIT
 */
@@ -580,12 +580,12 @@ require.register("vue/src/config.js", function(exports, require, module){
 var prefix = 'v',
     specialAttributes = [
         'pre',
+        'ref',
+        'with',
         'text',
         'repeat',
         'partial',
-        'with',
         'component',
-        'component-id',
         'transition'
     ],
     config = module.exports = {
@@ -749,7 +749,9 @@ var utils = module.exports = {
         node.innerHTML = template.trim()
         /* jshint boss: true */
         while (child = node.firstChild) {
-            frag.appendChild(child)
+            if (node.nodeType === 1) {
+                frag.appendChild(child)
+            }
         }
         return frag
     },
@@ -874,7 +876,7 @@ var Emitter     = require('./emitter'),
     hooks = [
         'created', 'ready',
         'beforeDestroy', 'afterDestroy',
-        'enteredView', 'leftView'
+        'attached', 'detached'
     ]
 
 /**
@@ -921,7 +923,7 @@ function Compiler (vm, options) {
     // set parent VM
     // and register child id on parent
     var parent = compiler.parentCompiler,
-        childId = utils.attr(el, 'component-id')
+        childId = utils.attr(el, 'ref')
     if (parent) {
         parent.childCompilers.push(compiler)
         def(vm, '$parent', parent.vm)
@@ -950,7 +952,7 @@ function Compiler (vm, options) {
     extend(data, vm)
 
     // observe the data
-    Observer.observe(data, '', compiler.observer)
+    compiler.observeData(data)
     
     // for repeated items, create an index binding
     // which should be inenumerable but configurable
@@ -960,21 +962,6 @@ function Compiler (vm, options) {
         compiler.createBinding('$index')
     }
 
-    // allow the $data object to be swapped
-    Object.defineProperty(vm, '$data', {
-        enumerable: false,
-        get: function () {
-            return compiler.data
-        },
-        set: function (newData) {
-            var oldData = compiler.data
-            Observer.unobserve(oldData, '', compiler.observer)
-            compiler.data = newData
-            Observer.copyPaths(newData, oldData)
-            Observer.observe(newData, '', compiler.observer)
-        }
-    })
-
     // now parse the DOM, during which we will create necessary bindings
     // and bind the parsed directives
     compiler.compile(el, true)
@@ -1053,21 +1040,10 @@ CompilerProto.setupObserver = function () {
 
     // add own listeners which trigger binding updates
     observer
-        .on('get', function (key) {
-            check(key)
-            DepsParser.catcher.emit('get', bindings[key])
-        })
-        .on('set', function (key, val) {
-            observer.emit('change:' + key, val)
-            check(key)
-            bindings[key].update(val)
-        })
-        .on('mutate', function (key, val, mutation) {
-            observer.emit('change:' + key, val, mutation)
-            check(key)
-            bindings[key].pub()
-        })
-    
+        .on('get', onGet)
+        .on('set', onSet)
+        .on('mutate', onSet)
+
     // register hooks
     hooks.forEach(function (hook) {
         var fns = options[hook]
@@ -1083,6 +1059,17 @@ CompilerProto.setupObserver = function () {
         }
     })
 
+    function onGet (key) {
+        check(key)
+        DepsParser.catcher.emit('get', bindings[key])
+    }
+
+    function onSet (key, val, mutation) {
+        observer.emit('change:' + key, val, mutation)
+        check(key)
+        bindings[key].update(val)
+    }
+
     function register (hook, fn) {
         observer.on('hook:' + hook, function () {
             fn.call(compiler.vm, options)
@@ -1096,6 +1083,48 @@ CompilerProto.setupObserver = function () {
     }
 }
 
+CompilerProto.observeData = function (data) {
+
+    var compiler = this,
+        observer = compiler.observer
+
+    // recursively observe nested properties
+    Observer.observe(data, '', observer)
+
+    // also create binding for top level $data
+    // so it can be used in templates too
+    var $dataBinding = compiler.bindings['$data'] = new Binding(compiler, '$data')
+    $dataBinding.update(data)
+
+    // allow $data to be swapped
+    Object.defineProperty(compiler.vm, '$data', {
+        enumerable: false,
+        get: function () {
+            compiler.observer.emit('get', '$data')
+            return compiler.data
+        },
+        set: function (newData) {
+            var oldData = compiler.data
+            Observer.unobserve(oldData, '', observer)
+            compiler.data = newData
+            Observer.copyPaths(newData, oldData)
+            Observer.observe(newData, '', observer)
+            compiler.observer.emit('set', '$data', newData)
+        }
+    })
+
+    // emit $data change on all changes
+    observer
+        .on('set', onSet)
+        .on('mutate', onSet)
+
+    function onSet (key) {
+        if (key !== '$data') {
+            $dataBinding.update(compiler.data)
+        }
+    }
+}
+
 /**
  *  Compile a DOM node (recursive)
  */
@@ -1317,8 +1346,7 @@ CompilerProto.bindDirective = function (directive) {
         compiler = compiler || this
         binding = compiler.bindings[key] || compiler.createBinding(key)
     }
-
-    binding.instances.push(directive)
+    binding.dirs.push(directive)
     directive.binding = binding
 
     // invoke bind hook if exists
@@ -1421,11 +1449,10 @@ CompilerProto.defineExp = function (key, binding) {
  */
 CompilerProto.defineComputed = function (key, binding, value) {
     this.markComputed(binding, value)
-    var def = {
+    Object.defineProperty(this.vm, key, {
         get: binding.value.$get,
         set: binding.value.$set
-    }
-    Object.defineProperty(this.vm, key, def)
+    })
 }
 
 /**
@@ -1501,7 +1528,7 @@ CompilerProto.destroy = function () {
     if (this.destroyed) return
 
     var compiler = this,
-        i, key, dir, instances, binding,
+        i, key, dir, dirs, binding,
         vm          = compiler.vm,
         el          = compiler.el,
         directives  = compiler.dirs,
@@ -1519,11 +1546,11 @@ CompilerProto.destroy = function () {
         dir = directives[i]
         // if this directive is an instance of an external binding
         // e.g. a directive that refers to a variable on the parent VM
-        // we need to remove it from that binding's instances
+        // we need to remove it from that binding's directives
         // * empty and literal bindings do not have binding.
         if (dir.binding && dir.binding.compiler !== compiler) {
-            instances = dir.binding.instances
-            if (instances) instances.splice(instances.indexOf(dir), 1)
+            dirs = dir.binding.dirs
+            if (dirs) dirs.splice(dirs.indexOf(dir), 1)
         }
         dir.unbind()
     }
@@ -1777,7 +1804,7 @@ function Binding (compiler, key, isExp, isFn) {
     this.root = !this.isExp && key.indexOf('.') === -1
     this.compiler = compiler
     this.key = key
-    this.instances = []
+    this.dirs = []
     this.subs = []
     this.deps = []
     this.unbound = false
@@ -1792,17 +1819,19 @@ BindingProto.update = function (value) {
     if (!this.isComputed || this.isFn) {
         this.value = value
     }
-    batcher.queue(this)
+    if (this.dirs.length || this.subs.length) {
+        batcher.queue(this)
+    }
 }
 
 /**
- *  Actually update the instances.
+ *  Actually update the directives.
  */
 BindingProto._update = function () {
-    var i = this.instances.length,
+    var i = this.dirs.length,
         value = this.val()
     while (i--) {
-        this.instances[i].update(value)
+        this.dirs[i].update(value)
     }
     this.pub()
 }
@@ -1837,9 +1866,9 @@ BindingProto.unbind = function () {
     // the batcher's flush queue when its owner
     // compiler has already been destroyed.
     this.unbound = true
-    var i = this.instances.length
+    var i = this.dirs.length
     while (i--) {
-        this.instances[i].unbind()
+        this.dirs[i].unbind()
     }
     i = this.deps.length
     var subs
@@ -1886,7 +1915,7 @@ var ArrayProxy = Object.create(Array.prototype)
 methods.forEach(function (method) {
     def(ArrayProxy, method, function () {
         var result = Array.prototype[method].apply(this, arguments)
-        this.__observer__.emit('mutate', this.__observer__.path, this, {
+        this.__observer__.emit('mutate', null, this, {
             method: method,
             args: slice.call(arguments),
             result: result
@@ -1963,13 +1992,12 @@ function watchObject (obj) {
  *  Watch an Array, overload mutation methods
  *  and add augmentations by intercepting the prototype chain
  */
-function watchArray (arr, path) {
+function watchArray (arr) {
     var observer = arr.__observer__
     if (!observer) {
         observer = new Emitter()
         def(arr, '__observer__', observer)
     }
-    observer.path = path
     if (hasProto) {
         arr.__proto__ = ArrayProxy
     } else {
@@ -2013,7 +2041,9 @@ function convert (obj, key) {
             unobserve(oldVal, key, observer)
             values[key] = newVal
             copyPaths(newVal, oldVal)
-            observer.emit('set', key, newVal)
+            // an immediate property should notify its parent
+            // to emit set for itself too
+            observer.emit('set', key, newVal, true)
             observe(newVal, key, observer)
         }
     })
@@ -2104,40 +2134,61 @@ function ensurePath (obj, key) {
  *  Observe an object with a given path,
  *  and proxy get/set/mutate events to the provided observer.
  */
-function observe (obj, rawPath, observer) {
+function observe (obj, rawPath, parentOb) {
+
     if (!isWatchable(obj)) return
+
     var path = rawPath ? rawPath + '.' : '',
-        ob, alreadyConverted = !!obj.__observer__
+        alreadyConverted = !!obj.__observer__,
+        childOb
+
     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] = {
+
+    childOb = obj.__observer__
+    childOb.values = childOb.values || utils.hash()
+
+    // setup proxy listeners on the parent observer.
+    // we need to keep reference to them so that they
+    // can be removed when the object is un-observed.
+    parentOb.proxies = parentOb.proxies || {}
+    var proxies = parentOb.proxies[path] = {
         get: function (key) {
-            observer.emit('get', path + key)
+            parentOb.emit('get', path + key)
         },
-        set: function (key, val) {
-            observer.emit('set', path + key, val)
+        set: function (key, val, propagate) {
+            parentOb.emit('set', path + key, val)
+            // also notify observer that the object itself changed
+            // but only do so when it's a immediate property. this
+            // avoids duplicate event firing.
+            if (rawPath && propagate) {
+                parentOb.emit('set', rawPath, obj, true)
+            }
         },
         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)
+            parentOb.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)
+                parentOb.emit('set', fixedPath + '.length', val.length)
             }
         }
     }
-    ob
+
+    // attach the listeners to the child observer.
+    // now all the events will propagate upwards.
+    childOb
         .on('get', proxies.get)
         .on('set', proxies.set)
         .on('mutate', proxies.mutate)
+
     if (alreadyConverted) {
+        // for objects that have already been converted,
+        // emit set events for everything inside
         emitSet(obj)
     } else {
         var type = typeOf(obj)
@@ -2153,14 +2204,20 @@ function observe (obj, rawPath, observer) {
  *  Cancel observation, turn off the listeners.
  */
 function unobserve (obj, path, observer) {
+
     if (!obj || !obj.__observer__) return
+
     path = path ? path + '.' : ''
     var proxies = observer.proxies[path]
     if (!proxies) return
+
+    // turn off listeners
     obj.__observer__
         .off('get', proxies.get)
         .off('set', proxies.set)
         .off('mutate', proxies.mutate)
+
+    // remove reference
     observer.proxies[path] = null
 }
 
@@ -2301,14 +2358,16 @@ function parseFilter (filter, compiler) {
  *  during initialization.
  */
 DirProto.update = function (value, init) {
-    if (!init && value === this.value) return
-    this.value = value
-    if (this._update) {
-        this._update(
-            this.filters
-                ? this.applyFilters(value)
-                : value
-        )
+    var type = utils.typeOf(value)
+    if (init || value !== this.value || type === 'Object' || type === 'Array') {
+        this.value = value
+        if (this._update) {
+            this._update(
+                this.filters
+                    ? this.applyFilters(value)
+                    : value
+            )
+        }
     }
 }
 
@@ -2743,7 +2802,7 @@ var transition = module.exports = function (el, stage, cb, compiler) {
 
     var changeState = function () {
         cb()
-        compiler.execHook(stage > 0 ? 'enteredView' : 'leftView')
+        compiler.execHook(stage > 0 ? 'attached' : 'detached')
     }
 
     if (compiler.init) {
@@ -3034,7 +3093,6 @@ module.exports = {
 });
 require.register("vue/src/directives/repeat.js", function(exports, require, module){
 var Observer   = require('../observer'),
-    Emitter    = require('../emitter'),
     utils      = require('../utils'),
     config     = require('../config'),
     transition = require('../transition'),
@@ -3126,7 +3184,7 @@ module.exports = {
         // extract transition information
         this.hasTrans = el.hasAttribute(config.attrs.transition)
         // extract child Id, if any
-        this.childId = utils.attr(el, 'component-id')
+        this.childId = utils.attr(el, 'ref')
 
         // create a comment node as a reference node for DOM insertions
         this.ref = document.createComment(config.prefix + '-repeat-' + this.key)
@@ -3142,7 +3200,10 @@ module.exports = {
             var method = mutation.method
             mutationHandlers[method].call(self, mutation)
             if (method !== 'push' && method !== 'pop') {
-                self.updateIndex()
+                var i = arr.length
+                while (i--) {
+                    arr[i].$index = i
+                }
             }
             if (method === 'push' || method === 'unshift' || method === 'splice') {
                 self.changed()
@@ -3152,6 +3213,8 @@ module.exports = {
     },
 
     update: function (collection, init) {
+        
+        if (collection === this.collection) return
 
         this.reset()
         // attach an object to container to hold handlers
@@ -3171,7 +3234,7 @@ module.exports = {
 
         // listen for collection mutation events
         // the collection has been augmented during Binding.set()
-        if (!collection.__observer__) Observer.watchArray(collection, null, new Emitter())
+        if (!collection.__observer__) Observer.watchArray(collection)
         collection.__observer__.on('mutate', this.mutationListener)
 
         // create child-vms and append to DOM
@@ -3192,6 +3255,7 @@ module.exports = {
         this.queued = true
         var self = this
         setTimeout(function () {
+            if (!self.compiler) return
             self.compiler.parseDeps()
             self.queued = false
         }, 0)
@@ -3258,16 +3322,6 @@ module.exports = {
         }
     },
 
-    /**
-     *  Update index of each item after a mutation
-     */
-    updateIndex: function () {
-        var i = this.vms.length
-        while (i--) {
-            this.vms[i].$data.$index = i
-        }
-    },
-
     reset: function () {
         if (this.childId) {
             delete this.vm.$[this.childId]

File diff ditekan karena terlalu besar
+ 1 - 1
dist/vue.min.js


+ 1 - 1
package.json

@@ -1,6 +1,6 @@
 {
   "name": "vue",
-  "version": "0.8.5",
+  "version": "0.8.6",
   "author": {
     "name": "Evan You",
     "email": "yyx990803@gmail.com",

+ 1 - 1
src/compiler.js

@@ -67,7 +67,7 @@ function Compiler (vm, options) {
     // set parent VM
     // and register child id on parent
     var parent = compiler.parentCompiler,
-        childId = utils.attr(el, 'component-id')
+        childId = utils.attr(el, 'ref')
     if (parent) {
         parent.childCompilers.push(compiler)
         def(vm, '$parent', parent.vm)

+ 2 - 2
src/config.js

@@ -1,12 +1,12 @@
 var prefix = 'v',
     specialAttributes = [
         'pre',
+        'ref',
+        'with',
         'text',
         'repeat',
         'partial',
-        'with',
         'component',
-        'component-id',
         'transition'
     ],
     config = module.exports = {

+ 1 - 1
src/directives/repeat.js

@@ -90,7 +90,7 @@ module.exports = {
         // extract transition information
         this.hasTrans = el.hasAttribute(config.attrs.transition)
         // extract child Id, if any
-        this.childId = utils.attr(el, 'component-id')
+        this.childId = utils.attr(el, 'ref')
 
         // create a comment node as a reference node for DOM insertions
         this.ref = document.createComment(config.prefix + '-repeat-' + this.key)

+ 1 - 1
test/functional/fixtures/repeated-vms.html

@@ -1,4 +1,4 @@
-<div class="item" v-repeat="items" v-component="item" v-component-id="items" v-on="click:click">
+<div class="item" v-repeat="items" v-component="item" v-ref="items" v-on="click:click">
     {{msg + ' ' + title}}
 </div>
 

+ 1 - 1
test/functional/specs/repeated-vms.js

@@ -30,7 +30,7 @@ casper.test.begin('Repeated ViewModels', 8, function (test) {
                 return app.$.items[0].reversed
             },
             'a init click click'.split('').reverse().join(''),
-            'should be able to access repeated vms with v-component-id'
+            'should be able to access repeated vms with v-ref'
         )
     })
     .run(function () {

+ 2 - 2
test/unit/specs/directives.js

@@ -610,7 +610,7 @@ describe('UNIT: Directives', function () {
 
     })
 
-    describe('component-id', function () {
+    describe('ref', function () {
         
         it('should register a VM isntance on its parent\'s $', function () {
             var called = false
@@ -622,7 +622,7 @@ describe('UNIT: Directives', function () {
                 }
             })
             var t = new Vue({
-                template: '<div v-component="child" v-component-id="hihi"></div>',
+                template: '<div v-component="child" v-ref="hihi"></div>',
                 components: {
                     child: Child
                 }

Beberapa file tidak ditampilkan karena terlalu banyak file yang berubah dalam diff ini