Просмотр исходного кода

improve prop default value handling (close #1032)

Evan You 11 лет назад
Родитель
Сommit
145fed71f9

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

@@ -106,11 +106,7 @@ function makePropsLinkFn (props) {
       options = prop.options
       if (prop.raw === null) {
         // initialize absent prop
-        vm._data[path] = options.type === Boolean
-          ? false
-          : options.hasOwnProperty('default')
-            ? options.default
-            : undefined
+        _.initProp(vm, prop, getDefault(options))
       } else if (prop.dynamic) {
         // dynamic prop
         if (vm._context) {
@@ -139,3 +135,34 @@ function makePropsLinkFn (props) {
     }
   }
 }
+
+/**
+ * Get the default value of a prop.
+ *
+ * @param {Object} options
+ * @return {*}
+ */
+
+function getDefault (options) {
+  // absent boolean value
+  if (options.type === Boolean) {
+    return false
+  }
+  // no default, return undefined
+  if (!options.hasOwnProperty('default')) {
+    return
+  }
+  var def = options.default
+  // warn against non-factory defaults for Object & Array
+  if (_.isObject(def)) {
+    process.env.NODE_ENV !== 'production' && _.warn(
+      'Object/Array as default prop values will be shared ' +
+      'across multiple instances. Use a factory function ' +
+      'to return the default value instead.'
+    )
+  }
+  // call factory function for non-Function types
+  return typeof def === 'function' && options.type !== Function
+    ? def()
+    : def
+}

+ 16 - 1
test/unit/specs/compiler/compile_spec.js

@@ -167,6 +167,15 @@ if (_.inBrowser) {
         {
           name: 'boolean-absent',
           type: Boolean
+        },
+        {
+          name: 'factory',
+          type: Object,
+          default: function () {
+            return {
+              a: 123
+            }
+          }
         }
       ].map(function (p) {
         return typeof p === 'string' ? { name: p } : p
@@ -219,7 +228,7 @@ if (_.inBrowser) {
       expect(args[3]).toBe(def)
       // literal and one time should've been set on the _data
       // and numbers should be casted
-      expect(Object.keys(vm._data).length).toBe(8)
+      expect(Object.keys(vm._data).length).toBe(9)
       expect(vm.a).toBe(1)
       expect(vm._data.a).toBe(1)
       expect(vm.someOtherAttr).toBe(2)
@@ -228,10 +237,16 @@ if (_.inBrowser) {
       expect(vm._data.onetime).toBe('from parent: a')
       expect(vm.booleanLiteral).toBe('from parent: true')
       expect(vm._data.booleanLiteral).toBe('from parent: true')
+      expect(vm.camelCase).toBe('hi')
       expect(vm._data.camelCase).toBe('hi')
+      expect(vm.defaultValue).toBe(123)
       expect(vm._data.defaultValue).toBe(123)
+      expect(vm.boolean).toBe(true)
       expect(vm._data.boolean).toBe(true)
+      expect(vm.booleanAbsent).toBe(false)
       expect(vm._data.booleanAbsent).toBe(false)
+      expect(vm.factory).toBe(vm._data.factory)
+      expect(vm.factory.a).toBe(123)
     })
 
     it('props on root instance', function () {

+ 18 - 0
test/unit/specs/directives/prop_spec.js

@@ -184,6 +184,24 @@ if (_.inBrowser) {
       expect(hasWarned(_, 'Props will not be compiled if no `el`')).toBe(true)
     })
 
+    it('warn object/array default values', function () {
+      new Vue({
+        el: el,
+        props: {
+          arr: {
+            type: Array,
+            default: []
+          },
+          obj: {
+            type: Object,
+            default: {}
+          }
+        }
+      })
+      expect(hasWarned(_, 'Use a factory function to return the default value')).toBe(true)
+      expect(_.warn.calls.count()).toBe(2)
+    })
+
     it('teardown', function (done) {
       var vm = new Vue({
         el: el,