From 306997eaf4ff36f4757c753c8a00ce3851e29cca Mon Sep 17 00:00:00 2001 From: Rahul Kadyan Date: Fri, 7 Jul 2017 09:09:25 +0530 Subject: [PATCH] fix(core): add merge strategy for provide option (#6025) Fix #6008 --- src/core/util/options.js | 36 +++++--- test/unit/features/options/inject.spec.js | 101 ++++++++++++++++++++++ 2 files changed, 126 insertions(+), 11 deletions(-) diff --git a/src/core/util/options.js b/src/core/util/options.js index 99580f0443a..3a86509ae74 100644 --- a/src/core/util/options.js +++ b/src/core/util/options.js @@ -63,7 +63,7 @@ function mergeData (to: Object, from: ?Object): Object { /** * Data */ -strats.data = function ( +export function mergeDataOrFn ( parentVal: any, childVal: any, vm?: Component @@ -73,15 +73,6 @@ strats.data = function ( if (!childVal) { return parentVal } - if (typeof childVal !== 'function') { - process.env.NODE_ENV !== 'production' && warn( - 'The "data" option should be a function ' + - 'that returns a per-instance value in component ' + - 'definitions.', - vm - ) - return parentVal - } if (!parentVal) { return childVal } @@ -92,7 +83,7 @@ strats.data = function ( // it has to be a function to pass previous merges. return function mergedDataFn () { return mergeData( - childVal.call(this), + typeof childVal === 'function' ? childVal.call(this) : childVal, parentVal.call(this) ) } @@ -114,6 +105,28 @@ strats.data = function ( } } +strats.data = function ( + parentVal: any, + childVal: any, + vm?: Component +): ?Function { + if (!vm) { + if (childVal && typeof childVal !== 'function') { + process.env.NODE_ENV !== 'production' && warn( + 'The "data" option should be a function ' + + 'that returns a per-instance value in component ' + + 'definitions.', + vm + ) + + return parentVal + } + return mergeDataOrFn.call(this, parentVal, childVal) + } + + return mergeDataOrFn(parentVal, childVal, vm) +} + /** * Hooks and props are merged as arrays. */ @@ -191,6 +204,7 @@ strats.computed = function (parentVal: ?Object, childVal: ?Object): ?Object { extend(ret, childVal) return ret } +strats.provide = mergeDataOrFn /** * Default strategy. diff --git a/test/unit/features/options/inject.spec.js b/test/unit/features/options/inject.spec.js index c366918bd94..e3ba23d488c 100644 --- a/test/unit/features/options/inject.spec.js +++ b/test/unit/features/options/inject.spec.js @@ -298,4 +298,105 @@ describe('Options provide/inject', () => { expect(`Injection "bar" not found`).not.toHaveBeenWarned() expect(`Injection "baz" not found`).not.toHaveBeenWarned() }) + + // Github issue #6008 + it('should merge provide from mixins (objects)', () => { + const mixinA = { provide: { foo: 'foo' }} + const mixinB = { provide: { bar: 'bar' }} + const child = { + inject: ['foo', 'bar'], + template: ``, + created () { + injected = [this.foo, this.bar] + } + } + new Vue({ + mixins: [mixinA, mixinB], + render (h) { + return h(child) + } + }).$mount() + + expect(injected).toEqual(['foo', 'bar']) + }) + it('should merge provide from mixins (functions)', () => { + const mixinA = { provide: () => ({ foo: 'foo' }) } + const mixinB = { provide: () => ({ bar: 'bar' }) } + const child = { + inject: ['foo', 'bar'], + template: ``, + created () { + injected = [this.foo, this.bar] + } + } + new Vue({ + mixins: [mixinA, mixinB], + render (h) { + return h(child) + } + }).$mount() + + expect(injected).toEqual(['foo', 'bar']) + }) + it('should merge provide from mixins (mix of objects and functions)', () => { + const mixinA = { provide: { foo: 'foo' }} + const mixinB = { provide: () => ({ bar: 'bar' }) } + const mixinC = { provide: { baz: 'baz' }} + const mixinD = { provide: () => ({ bam: 'bam' }) } + const child = { + inject: ['foo', 'bar', 'baz', 'bam'], + template: ``, + created () { + injected = [this.foo, this.bar, this.baz, this.bam] + } + } + new Vue({ + mixins: [mixinA, mixinB, mixinC, mixinD], + render (h) { + return h(child) + } + }).$mount() + + expect(injected).toEqual(['foo', 'bar', 'baz', 'bam']) + }) + it('should merge provide from mixins and override existing keys', () => { + const mixinA = { provide: { foo: 'foo' }} + const mixinB = { provide: { foo: 'bar' }} + const child = { + inject: ['foo'], + template: ``, + created () { + injected = [this.foo] + } + } + new Vue({ + mixins: [mixinA, mixinB], + render (h) { + return h(child) + } + }).$mount() + + expect(injected).toEqual(['bar']) + }) + it('should merge provide when Vue.extend', () => { + const mixinA = { provide: () => ({ foo: 'foo' }) } + const child = { + inject: ['foo', 'bar'], + template: ``, + created () { + injected = [this.foo, this.bar] + } + } + const Ctor = Vue.extend({ + mixins: [mixinA], + provide: { bar: 'bar' }, + render (h) { + return h(child) + } + }) + + new Ctor().$mount() + + expect(injected).toEqual(['foo', 'bar']) + }) })