From 2689a64ccade05ced048eab1f62e8317fe8d2b6e Mon Sep 17 00:00:00 2001 From: hzzhaoxinhui Date: Thu, 24 Aug 2017 15:54:31 +0800 Subject: [PATCH 1/3] fix is undefined when component has no props bug (#6263) --- flow/component.js | 2 +- src/core/instance/render.js | 5 +++-- test/unit/features/instance/properties.spec.js | 14 ++++++++++++++ 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/flow/component.js b/flow/component.js index 08fcfd4a6ba..4569f73df6a 100644 --- a/flow/component.js +++ b/flow/component.js @@ -29,7 +29,7 @@ declare interface Component { $slots: { [key: string]: Array }; $scopedSlots: { [key: string]: () => VNodeChildren }; $vnode: VNode; // the placeholder node for the component in parent's render tree - $attrs: ?{ [key: string] : string }; + $attrs: { [key: string] : string }; $listeners: ?{ [key: string]: Function | Array }; $isServer: boolean; diff --git a/src/core/instance/render.js b/src/core/instance/render.js index cf2e8fe89a4..7cc33a52da1 100644 --- a/src/core/instance/render.js +++ b/src/core/instance/render.js @@ -49,16 +49,17 @@ export function initRender (vm: Component) { // $attrs & $listeners are exposed for easier HOC creation. // they need to be reactive so that HOCs using them are always updated const parentData = parentVnode && parentVnode.data + /* istanbul ignore else */ if (process.env.NODE_ENV !== 'production') { - defineReactive(vm, '$attrs', parentData && parentData.attrs, () => { + defineReactive(vm, '$attrs', parentData && parentData.attrs || {}, () => { !isUpdatingChildComponent && warn(`$attrs is readonly.`, vm) }, true) defineReactive(vm, '$listeners', vm.$options._parentListeners, () => { !isUpdatingChildComponent && warn(`$listeners is readonly.`, vm) }, true) } else { - defineReactive(vm, '$attrs', parentData && parentData.attrs, null, true) + defineReactive(vm, '$attrs', parentData && parentData.attrs || {}, null, true) defineReactive(vm, '$listeners', vm.$options._parentListeners, null, true) } } diff --git a/test/unit/features/instance/properties.spec.js b/test/unit/features/instance/properties.spec.js index f14aa52a863..02aca12fcfa 100644 --- a/test/unit/features/instance/properties.spec.js +++ b/test/unit/features/instance/properties.spec.js @@ -146,6 +146,20 @@ describe('Instance properties', () => { }).then(done) }) + // #6263 + it('$attrs should not be undefined when no props passed in', () => { + const vm = new Vue({ + template: ``, + data: { foo: 'foo' }, + components: { + foo: { + template: `
{{ this.foo }}
` + } + } + }).$mount() + expect(vm.$attrs).toBeDefined() + }) + it('warn mutating $attrs', () => { const vm = new Vue() vm.$attrs = {} From e1ede75ea108643fae86aac318e6d0dc95ea61e7 Mon Sep 17 00:00:00 2001 From: Evan You Date: Tue, 29 Aug 2017 22:35:25 +0200 Subject: [PATCH 2/3] Update render.js --- src/core/instance/render.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/core/instance/render.js b/src/core/instance/render.js index 7cc33a52da1..62b4878195b 100644 --- a/src/core/instance/render.js +++ b/src/core/instance/render.js @@ -52,15 +52,15 @@ export function initRender (vm: Component) { /* istanbul ignore else */ if (process.env.NODE_ENV !== 'production') { - defineReactive(vm, '$attrs', parentData && parentData.attrs || {}, () => { + defineReactive(vm, '$attrs', parentData && parentData.attrs || emptyObject, () => { !isUpdatingChildComponent && warn(`$attrs is readonly.`, vm) }, true) - defineReactive(vm, '$listeners', vm.$options._parentListeners, () => { + defineReactive(vm, '$listeners', vm.$options._parentListeners || emptyObject, () => { !isUpdatingChildComponent && warn(`$listeners is readonly.`, vm) }, true) } else { - defineReactive(vm, '$attrs', parentData && parentData.attrs || {}, null, true) - defineReactive(vm, '$listeners', vm.$options._parentListeners, null, true) + defineReactive(vm, '$attrs', parentData && parentData.attrs || emptyObject, null, true) + defineReactive(vm, '$listeners', vm.$options._parentListeners || emptyObject, null, true) } } From c55aadfcb152bf4ac41465244c7f1525b317209d Mon Sep 17 00:00:00 2001 From: Evan You Date: Tue, 29 Aug 2017 22:54:20 +0200 Subject: [PATCH 3/3] fix: ensure $listeners is also an object + fix types --- flow/component.js | 2 +- src/core/instance/lifecycle.js | 4 ++-- types/vue.d.ts | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/flow/component.js b/flow/component.js index 4569f73df6a..381943edaf7 100644 --- a/flow/component.js +++ b/flow/component.js @@ -30,7 +30,7 @@ declare interface Component { $scopedSlots: { [key: string]: () => VNodeChildren }; $vnode: VNode; // the placeholder node for the component in parent's render tree $attrs: { [key: string] : string }; - $listeners: ?{ [key: string]: Function | Array }; + $listeners: { [key: string]: Function | Array }; $isServer: boolean; // public methods diff --git a/src/core/instance/lifecycle.js b/src/core/instance/lifecycle.js index fd2c9e5b7d4..2f923c0792d 100644 --- a/src/core/instance/lifecycle.js +++ b/src/core/instance/lifecycle.js @@ -232,8 +232,8 @@ export function updateChildComponent ( // update $attrs and $listensers hash // these are also reactive so they may trigger child update if the child // used them during render - vm.$attrs = parentVnode.data && parentVnode.data.attrs - vm.$listeners = listeners + vm.$attrs = (parentVnode.data && parentVnode.data.attrs) || emptyObject + vm.$listeners = listeners || emptyObject // update props if (propsData && vm.$options.props) { diff --git a/types/vue.d.ts b/types/vue.d.ts index 0bfce6ae45d..111f5fe99f4 100644 --- a/types/vue.d.ts +++ b/types/vue.d.ts @@ -45,8 +45,8 @@ export declare class Vue { readonly $ssrContext: any; readonly $props: any; readonly $vnode: VNode; - readonly $attrs: { [key: string] : string } | void; - readonly $listeners: { [key: string]: Function | Array } | void; + readonly $attrs: { [key: string] : string }; + readonly $listeners: { [key: string]: Function | Array }; $mount(elementOrSelector?: Element | String, hydrating?: boolean): this; $forceUpdate(): void;