From 145fed71f92bebab6b14b6c5bbe584c6aecaca25 Mon Sep 17 00:00:00 2001 From: Evan You Date: Fri, 10 Jul 2015 12:04:32 +0800 Subject: [PATCH] improve prop default value handling (close #1032) --- src/compiler/compile-props.js | 37 ++++++++++++++++++++---- test/unit/specs/compiler/compile_spec.js | 17 ++++++++++- test/unit/specs/directives/prop_spec.js | 18 ++++++++++++ 3 files changed, 66 insertions(+), 6 deletions(-) diff --git a/src/compiler/compile-props.js b/src/compiler/compile-props.js index 79a417574cd..a81440214de 100644 --- a/src/compiler/compile-props.js +++ b/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 +} diff --git a/test/unit/specs/compiler/compile_spec.js b/test/unit/specs/compiler/compile_spec.js index 2b59ea4139f..b71eed08430 100644 --- a/test/unit/specs/compiler/compile_spec.js +++ b/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 () { diff --git a/test/unit/specs/directives/prop_spec.js b/test/unit/specs/directives/prop_spec.js index 40112ee4e84..dfd6c515b49 100644 --- a/test/unit/specs/directives/prop_spec.js +++ b/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,