@@ -8,12 +8,16 @@ import {
8
8
isFunction ,
9
9
extend
10
10
} from '@vue/shared'
11
- import { ComponentInternalInstance , ConcreteComponent } from './component'
11
+ import {
12
+ ComponentInternalInstance ,
13
+ ComponentOptions ,
14
+ ConcreteComponent
15
+ } from './component'
12
16
import { callWithAsyncErrorHandling , ErrorCodes } from './errorHandling'
13
17
import { warn } from './warning'
14
- import { normalizePropsOptions } from './componentProps'
15
18
import { UnionToIntersection } from './helpers/typeUtils'
16
19
import { devtoolsComponentEmit } from './devtools'
20
+ import { AppContext } from './apiCreateApp'
17
21
18
22
export type ObjectEmitsOptions = Record <
19
23
string ,
@@ -44,18 +48,20 @@ export function emit(
44
48
const props = instance . vnode . props || EMPTY_OBJ
45
49
46
50
if ( __DEV__ ) {
47
- const options = normalizeEmitsOptions ( instance . type )
48
- if ( options ) {
49
- if ( ! ( event in options ) ) {
50
- const propsOptions = normalizePropsOptions ( instance . type ) [ 0 ]
51
+ const {
52
+ emitsOptions,
53
+ propsOptions : [ propsOptions ]
54
+ } = instance
55
+ if ( emitsOptions ) {
56
+ if ( ! ( event in emitsOptions ) ) {
51
57
if ( ! propsOptions || ! ( `on` + capitalize ( event ) in propsOptions ) ) {
52
58
warn (
53
59
`Component emitted event "${ event } " but it is neither declared in ` +
54
60
`the emits option nor as an "on${ capitalize ( event ) } " prop.`
55
61
)
56
62
}
57
63
} else {
58
- const validator = options [ event ]
64
+ const validator = emitsOptions [ event ]
59
65
if ( isFunction ( validator ) ) {
60
66
const isValid = validator ( ...args )
61
67
if ( ! isValid ) {
@@ -98,11 +104,16 @@ export function emit(
98
104
}
99
105
}
100
106
101
- function normalizeEmitsOptions (
102
- comp : ConcreteComponent
103
- ) : ObjectEmitsOptions | undefined {
104
- if ( hasOwn ( comp , '__emits' ) ) {
105
- return comp . __emits
107
+ export function normalizeEmitsOptions (
108
+ comp : ConcreteComponent ,
109
+ appContext : AppContext ,
110
+ asMixin = false
111
+ ) : ObjectEmitsOptions | null {
112
+ const appId = appContext . app ? appContext . app . _uid : - 1
113
+ const cache = comp . __emits || ( comp . __emits = { } )
114
+ const cached = cache [ appId ]
115
+ if ( cached !== undefined ) {
116
+ return cached
106
117
}
107
118
108
119
const raw = comp . emits
@@ -111,39 +122,46 @@ function normalizeEmitsOptions(
111
122
// apply mixin/extends props
112
123
let hasExtends = false
113
124
if ( __FEATURE_OPTIONS_API__ && ! isFunction ( comp ) ) {
114
- if ( comp . extends ) {
125
+ const extendEmits = ( raw : ComponentOptions ) => {
115
126
hasExtends = true
116
- extend ( normalized , normalizeEmitsOptions ( comp . extends ) )
127
+ extend ( normalized , normalizeEmitsOptions ( raw , appContext , true ) )
128
+ }
129
+ if ( ! asMixin && appContext . mixins . length ) {
130
+ appContext . mixins . forEach ( extendEmits )
131
+ }
132
+ if ( comp . extends ) {
133
+ extendEmits ( comp . extends )
117
134
}
118
135
if ( comp . mixins ) {
119
- hasExtends = true
120
- comp . mixins . forEach ( m => extend ( normalized , normalizeEmitsOptions ( m ) ) )
136
+ comp . mixins . forEach ( extendEmits )
121
137
}
122
138
}
123
139
124
140
if ( ! raw && ! hasExtends ) {
125
- return ( comp . __emits = undefined )
141
+ return ( cache [ appId ] = null )
126
142
}
127
143
128
144
if ( isArray ( raw ) ) {
129
145
raw . forEach ( key => ( normalized [ key ] = null ) )
130
146
} else {
131
147
extend ( normalized , raw )
132
148
}
133
- return ( comp . __emits = normalized )
149
+ return ( cache [ appId ] = normalized )
134
150
}
135
151
136
152
// Check if an incoming prop key is a declared emit event listener.
137
153
// e.g. With `emits: { click: null }`, props named `onClick` and `onclick` are
138
154
// both considered matched listeners.
139
- export function isEmitListener ( comp : ConcreteComponent , key : string ) : boolean {
140
- let emits : ObjectEmitsOptions | undefined
141
- if ( ! isOn ( key ) || ! ( emits = normalizeEmitsOptions ( comp ) ) ) {
155
+ export function isEmitListener (
156
+ options : ObjectEmitsOptions | null ,
157
+ key : string
158
+ ) : boolean {
159
+ if ( ! options || ! isOn ( key ) ) {
142
160
return false
143
161
}
144
162
key = key . replace ( / O n c e $ / , '' )
145
163
return (
146
- hasOwn ( emits , key [ 2 ] . toLowerCase ( ) + key . slice ( 3 ) ) ||
147
- hasOwn ( emits , key . slice ( 2 ) )
164
+ hasOwn ( options , key [ 2 ] . toLowerCase ( ) + key . slice ( 3 ) ) ||
165
+ hasOwn ( options , key . slice ( 2 ) )
148
166
)
149
167
}
0 commit comments