Skip to content

Commit 6bcdaa6

Browse files
zxc0328ztlevi
authored andcommitted
fix: ensure $attrs and $listeners are always objects (vuejs#6441)
fix vuejs#6263
1 parent 0d6cbff commit 6bcdaa6

File tree

5 files changed

+25
-10
lines changed

5 files changed

+25
-10
lines changed

flow/component.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ declare interface Component {
2929
$slots: { [key: string]: Array<VNode> };
3030
$scopedSlots: { [key: string]: () => VNodeChildren };
3131
$vnode: VNode; // the placeholder node for the component in parent's render tree
32-
$attrs: ?{ [key: string] : string };
33-
$listeners: ?{ [key: string]: Function | Array<Function> };
32+
$attrs: { [key: string] : string };
33+
$listeners: { [key: string]: Function | Array<Function> };
3434
$isServer: boolean;
3535

3636
// public methods

src/core/instance/lifecycle.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -232,8 +232,8 @@ export function updateChildComponent (
232232
// update $attrs and $listensers hash
233233
// these are also reactive so they may trigger child update if the child
234234
// used them during render
235-
vm.$attrs = parentVnode.data && parentVnode.data.attrs
236-
vm.$listeners = listeners
235+
vm.$attrs = (parentVnode.data && parentVnode.data.attrs) || emptyObject
236+
vm.$listeners = listeners || emptyObject
237237

238238
// update props
239239
if (propsData && vm.$options.props) {

src/core/instance/render.js

+5-4
Original file line numberDiff line numberDiff line change
@@ -49,17 +49,18 @@ export function initRender (vm: Component) {
4949
// $attrs & $listeners are exposed for easier HOC creation.
5050
// they need to be reactive so that HOCs using them are always updated
5151
const parentData = parentVnode && parentVnode.data
52+
5253
/* istanbul ignore else */
5354
if (process.env.NODE_ENV !== 'production') {
54-
defineReactive(vm, '$attrs', parentData && parentData.attrs, () => {
55+
defineReactive(vm, '$attrs', parentData && parentData.attrs || emptyObject, () => {
5556
!isUpdatingChildComponent && warn(`$attrs is readonly.`, vm)
5657
}, true)
57-
defineReactive(vm, '$listeners', vm.$options._parentListeners, () => {
58+
defineReactive(vm, '$listeners', vm.$options._parentListeners || emptyObject, () => {
5859
!isUpdatingChildComponent && warn(`$listeners is readonly.`, vm)
5960
}, true)
6061
} else {
61-
defineReactive(vm, '$attrs', parentData && parentData.attrs, null, true)
62-
defineReactive(vm, '$listeners', vm.$options._parentListeners, null, true)
62+
defineReactive(vm, '$attrs', parentData && parentData.attrs || emptyObject, null, true)
63+
defineReactive(vm, '$listeners', vm.$options._parentListeners || emptyObject, null, true)
6364
}
6465
}
6566

test/unit/features/instance/properties.spec.js

+14
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,20 @@ describe('Instance properties', () => {
146146
}).then(done)
147147
})
148148

149+
// #6263
150+
it('$attrs should not be undefined when no props passed in', () => {
151+
const vm = new Vue({
152+
template: `<foo/>`,
153+
data: { foo: 'foo' },
154+
components: {
155+
foo: {
156+
template: `<div>{{ this.foo }}</div>`
157+
}
158+
}
159+
}).$mount()
160+
expect(vm.$attrs).toBeDefined()
161+
})
162+
149163
it('warn mutating $attrs', () => {
150164
const vm = new Vue()
151165
vm.$attrs = {}

types/vue.d.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,8 @@ export declare class Vue {
4545
readonly $ssrContext: any;
4646
readonly $props: any;
4747
readonly $vnode: VNode;
48-
readonly $attrs: { [key: string] : string } | void;
49-
readonly $listeners: { [key: string]: Function | Array<Function> } | void;
48+
readonly $attrs: { [key: string] : string };
49+
readonly $listeners: { [key: string]: Function | Array<Function> };
5050

5151
$mount(elementOrSelector?: Element | String, hydrating?: boolean): this;
5252
$forceUpdate(): void;

0 commit comments

Comments
 (0)