Browse Source

fix(core): add merge strategy for provide option (#6025)

Fix #6008
Rahul Kadyan 9 years ago
parent
commit
306997eaf4
2 changed files with 126 additions and 11 deletions
  1. 25 11
      src/core/util/options.js
  2. 101 0
      test/unit/features/options/inject.spec.js

+ 25 - 11
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.

+ 101 - 0
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: `<span/>`,
+      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: `<span/>`,
+      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: `<span/>`,
+      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: `<span/>`,
+      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: `<span/>`,
+      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'])
+  })
 })