diff --git a/src/core/instance/inject.js b/src/core/instance/inject.js index 0b1a071e3c4..a2954163140 100644 --- a/src/core/instance/inject.js +++ b/src/core/instance/inject.js @@ -1,6 +1,8 @@ /* @flow */ import { hasSymbol } from 'core/util/env' +import { warn } from '../util/index' +import { defineReactive } from '../observer/index' export function initProvide (vm: Component) { const provide = vm.$options.provide @@ -29,7 +31,18 @@ export function initInjections (vm: Component) { let source = vm while (source) { if (source._provided && provideKey in source._provided) { - vm[key] = source._provided[provideKey] + if (process.env.NODE_ENV !== 'production') { + defineReactive(vm, key, source._provided[provideKey], () => { + warn( + `Avoid mutating a injections directly since the value will be ` + + `overwritten whenever the provided component re-renders. ` + + `injections being mutated: "${key}"`, + vm + ) + }) + } else { + defineReactive(vm, key, source._provided[provideKey]) + } break } source = source.$parent diff --git a/test/unit/features/options/inject.spec.js b/test/unit/features/options/inject.spec.js index ac67749f43d..bfb5e5bd143 100644 --- a/test/unit/features/options/inject.spec.js +++ b/test/unit/features/options/inject.spec.js @@ -162,4 +162,59 @@ describe('Options provide/inject', () => { expect(vm.$el.textContent).toBe('123') }) } + + // Github issue #5223 + it('should work with reactive array', done => { + const vm = new Vue({ + template: `
`, + data () { + return { + foo: [] + } + }, + provide () { + return { + foo: this.foo + } + }, + components: { + child: { + inject: ['foo'], + template: `{{foo.length}}` + } + } + }).$mount() + + expect(vm.$el.innerHTML).toEqual(`0`) + vm.foo.push(vm.foo.length) + vm.$nextTick(() => { + expect(vm.$el.innerHTML).toEqual(`1`) + vm.foo.pop() + vm.$nextTick(() => { + expect(vm.$el.innerHTML).toEqual(`0`) + done() + }) + }) + }) + + it('should warn when injections has been modified', () => { + const key = 'foo' + const vm = new Vue({ + provide: { + foo: 1 + } + }) + + const child = new Vue({ + parent: vm, + inject: ['foo'] + }) + + expect(child.foo).toBe(1) + child.foo = 2 + expect( + `Avoid mutating a injections directly since the value will be ` + + `overwritten whenever the provided component re-renders. ` + + `injections being mutated: "${key}"`).toHaveBeenWarned() + }) })