Browse Source

props: adjust props merging strategy (fix #1431)

Evan You 10 years ago
parent
commit
02655cd931

+ 5 - 3
src/compiler/compile-props.js

@@ -2,6 +2,7 @@ var _ = require('../util')
 var dirParser = require('../parsers/directive')
 var propDef = require('../directives/internal/prop')
 var propBindingModes = require('../config')._propBindingModes
+var empty = {}
 
 // regexes
 var identRE = require('../parsers/path').identRE
@@ -18,11 +19,12 @@ var settablePathRE = /^[A-Za-z_$][\w$]*(\.[A-Za-z_$][\w$]*|\[[^\[\]]+\])*$/
 
 module.exports = function compileProps (el, propOptions) {
   var props = []
-  var i = propOptions.length
+  var names = Object.keys(propOptions)
+  var i = names.length
   var options, name, attr, value, path, parsed, prop
   while (i--) {
-    options = propOptions[i]
-    name = options.name
+    name = names[i]
+    options = propOptions[name] || empty
 
     if (process.env.NODE_ENV !== 'production' && name === '$data') {
       _.warn('Do not use $data as prop.')

+ 19 - 17
src/util/options.js

@@ -116,8 +116,7 @@ strats.detached =
 strats.beforeCompile =
 strats.compiled =
 strats.beforeDestroy =
-strats.destroyed =
-strats.props = function (parentVal, childVal) {
+strats.destroyed = function (parentVal, childVal) {
   return childVal
     ? parentVal
       ? parentVal.concat(childVal)
@@ -188,11 +187,13 @@ strats.events = function (parentVal, childVal) {
  * Other object hashes.
  */
 
+strats.props =
 strats.methods =
 strats.computed = function (parentVal, childVal) {
   if (!childVal) return parentVal
   if (!parentVal) return childVal
-  var ret = Object.create(parentVal)
+  var ret = Object.create(null)
+  extend(ret, parentVal)
   extend(ret, childVal)
   return ret
 }
@@ -247,21 +248,22 @@ function guardComponents (options) {
 
 function guardProps (options) {
   var props = options.props
-  if (_.isPlainObject(props)) {
-    options.props = Object.keys(props).map(function (key) {
-      var val = props[key]
-      if (!_.isPlainObject(val)) {
-        val = { type: val }
+  var i
+  if (_.isArray(props)) {
+    options.props = {}
+    i = props.length
+    while (i--) {
+      options.props[props[i]] = null
+    }
+  } else if (_.isPlainObject(props)) {
+    var keys = Object.keys(props)
+    i = keys.length
+    while (i--) {
+      var val = props[keys[i]]
+      if (typeof val === 'function') {
+        props[keys[i]] = { type: val }
       }
-      val.name = key
-      return val
-    })
-  } else if (_.isArray(props)) {
-    options.props = props.map(function (prop) {
-      return typeof prop === 'string'
-        ? { name: prop }
-        : prop
-    })
+    }
   }
 }
 

+ 9 - 12
test/unit/specs/compiler/compile_spec.js

@@ -239,14 +239,14 @@ if (_.inBrowser) {
 
     it('props', function () {
       var bindingModes = Vue.config._propBindingModes
-      var props = [
-        { name: 'testNormal' },
-        { name: 'testLiteral' },
-        { name: 'testTwoWay' },
-        { name: 'twoWayWarn' },
-        { name: 'testOneTime' },
-        { name: 'optimizeLiteral' }
-      ]
+      var props = {
+        testNormal: null,
+        testLiteral: null,
+        testTwoWay: null,
+        twoWayWarn: null,
+        testOneTime: null,
+        optimizeLiteral: null
+      }
       el.innerHTML = '<div ' +
         'v-bind:test-normal="a" ' +
         'test-literal="1" ' +
@@ -288,10 +288,7 @@ if (_.inBrowser) {
       vm._context = null
       el.setAttribute('v-bind:a', '"hi"')
       el.setAttribute(':b', 'hi')
-      compiler.compileAndLinkProps(vm, el, [
-        { name: 'a' },
-        { name: 'b' }
-      ])
+      compiler.compileAndLinkProps(vm, el, { a: null, b: null })
       expect(vm._bindDir.calls.count()).toBe(0)
       expect(vm._data.a).toBe('hi')
       expect(hasWarned(_, 'Cannot bind dynamic prop on a root')).toBe(true)

+ 6 - 10
test/unit/specs/directives/internal/prop_spec.js

@@ -306,13 +306,12 @@ if (_.inBrowser) {
           },
           components: {
             test: {
-              props: [
-                {
-                  name: 'test',
+              props: {
+                test: {
                   type: type,
                   validator: validator
                 }
-              ]
+              }
             }
           }
         })
@@ -400,12 +399,9 @@ if (_.inBrowser) {
           template: '<test></test>',
           components: {
             test: {
-              props: [
-                {
-                  name: 'prop',
-                  required: true
-                }
-              ]
+              props: {
+                prop: { required: true }
+              }
             }
           }
         })

+ 34 - 7
test/unit/specs/util/options_spec.js

@@ -18,7 +18,7 @@ describe('Util - Option merging', function () {
     expect(res).toBe(false)
   })
 
-  it('hooks & props', function () {
+  it('hooks', function () {
     var fn1 = function () {}
     var fn2 = function () {}
     var res
@@ -38,12 +38,6 @@ describe('Util - Option merging', function () {
     expect(res.length).toBe(2)
     expect(res[0]).toBe(fn1)
     expect(res[1]).toBe(fn2)
-    // both arrays
-    res = merge({props: [1]}, {props: [2]}).props
-    expect(Array.isArray(res)).toBe(true)
-    expect(res.length).toBe(2)
-    expect(res[0]).toBe(1)
-    expect(res[1]).toBe(2)
   })
 
   it('events', function () {
@@ -114,6 +108,39 @@ describe('Util - Option merging', function () {
     expect(res.b).toBe(asset2)
   })
 
+  it('props', function () {
+    var res = merge({
+      props: {
+        a: null,
+        d: null
+      }
+    }, {
+      props: {
+        a: { required: true },
+        b: Boolean,
+        c: { type: Array }
+      }
+    })
+    expect(typeof res.props.a).toBe('object')
+    expect(res.props.a.required).toBe(true)
+    expect(typeof res.props.b).toBe('object')
+    expect(res.props.b.type).toBe(Boolean)
+    expect(typeof res.props.c).toBe('object')
+    expect(res.props.c.type).toBe(Array)
+    expect(res.props.d).toBe(null)
+
+    // check array syntax
+    res = merge({
+      props: {
+        b: null
+      }
+    }, {
+      props: ['a']
+    })
+    expect(res.props.a).toBe(null)
+    expect(res.props.b).toBe(null)
+  })
+
   it('guard components', function () {
     var res = merge({
       components: null