@@ -14,7 +14,7 @@ import {
14
14
isVNode
15
15
} from './vnode'
16
16
import { handleError , ErrorCodes } from './errorHandling'
17
- import { PatchFlags , ShapeFlags , isOn } from '@vue/shared'
17
+ import { PatchFlags , ShapeFlags , isOn , isModelListener } from '@vue/shared'
18
18
import { warn } from './warning'
19
19
import { isHmrUpdating } from './hmr'
20
20
@@ -104,7 +104,9 @@ export function renderComponentRoot(
104
104
)
105
105
: render ( props , null as any /* we know it doesn't need it */ )
106
106
)
107
- fallthroughAttrs = Component . props ? attrs : getFallthroughAttrs ( attrs )
107
+ fallthroughAttrs = Component . props
108
+ ? attrs
109
+ : getFunctionalFallthrough ( attrs )
108
110
}
109
111
110
112
// attr merging
@@ -116,50 +118,56 @@ export function renderComponentRoot(
116
118
; [ root , setRoot ] = getChildRoot ( result )
117
119
}
118
120
119
- if (
120
- Component . inheritAttrs !== false &&
121
- fallthroughAttrs &&
122
- Object . keys ( fallthroughAttrs ) . length
123
- ) {
124
- if (
125
- root . shapeFlag & ShapeFlags . ELEMENT ||
126
- root . shapeFlag & ShapeFlags . COMPONENT
127
- ) {
128
- root = cloneVNode ( root , fallthroughAttrs )
129
- } else if ( __DEV__ && ! accessedAttrs && root . type !== Comment ) {
130
- const allAttrs = Object . keys ( attrs )
131
- const eventAttrs : string [ ] = [ ]
132
- const extraAttrs : string [ ] = [ ]
133
- for ( let i = 0 , l = allAttrs . length ; i < l ; i ++ ) {
134
- const key = allAttrs [ i ]
135
- if ( isOn ( key ) ) {
136
- // ignore v-model handlers when they fail to fallthrough
137
- if ( ! key . startsWith ( 'onUpdate:' ) ) {
138
- // remove `on`, lowercase first letter to reflect event casing
139
- // accurately
140
- eventAttrs . push ( key [ 2 ] . toLowerCase ( ) + key . slice ( 3 ) )
121
+ if ( Component . inheritAttrs !== false && fallthroughAttrs ) {
122
+ const keys = Object . keys ( fallthroughAttrs )
123
+ const { shapeFlag } = root
124
+ if ( keys . length ) {
125
+ if (
126
+ shapeFlag & ShapeFlags . ELEMENT ||
127
+ shapeFlag & ShapeFlags . COMPONENT
128
+ ) {
129
+ if ( shapeFlag & ShapeFlags . ELEMENT && keys . some ( isModelListener ) ) {
130
+ // #1643, #1543
131
+ // component v-model listeners should only fallthrough for component
132
+ // HOCs
133
+ fallthroughAttrs = filterModelListeners ( fallthroughAttrs )
134
+ }
135
+ root = cloneVNode ( root , fallthroughAttrs )
136
+ } else if ( __DEV__ && ! accessedAttrs && root . type !== Comment ) {
137
+ const allAttrs = Object . keys ( attrs )
138
+ const eventAttrs : string [ ] = [ ]
139
+ const extraAttrs : string [ ] = [ ]
140
+ for ( let i = 0 , l = allAttrs . length ; i < l ; i ++ ) {
141
+ const key = allAttrs [ i ]
142
+ if ( isOn ( key ) ) {
143
+ // ignore v-model handlers when they fail to fallthrough
144
+ if ( ! isModelListener ( key ) ) {
145
+ // remove `on`, lowercase first letter to reflect event casing
146
+ // accurately
147
+ eventAttrs . push ( key [ 2 ] . toLowerCase ( ) + key . slice ( 3 ) )
148
+ }
149
+ } else {
150
+ extraAttrs . push ( key )
141
151
}
142
- } else {
143
- extraAttrs . push ( key )
144
152
}
145
- }
146
- if ( extraAttrs . length ) {
147
- warn (
148
- `Extraneous non-props attributes ( ` +
149
- ` ${ extraAttrs . join ( ', ' ) } ) ` +
150
- `were passed to component but could not be automatically inherited ` +
151
- `because component renders fragment or text root nodes.`
152
- )
153
- }
154
- if ( eventAttrs . length ) {
155
- warn (
156
- `Extraneous non-emits event listeners ( ` +
157
- ` ${ eventAttrs . join ( ', ' ) } ) ` +
158
- `were passed to component but could not be automatically inherited ` +
159
- `because component renders fragment or text root nodes. ` +
160
- `If the listener is intended to be a component custom event listener only, ` +
161
- `declare it using the "emits" option.`
162
- )
153
+ if ( extraAttrs . length ) {
154
+ warn (
155
+ `Extraneous non-props attributes (` +
156
+ ` ${ extraAttrs . join ( ', ' ) } ) ` +
157
+ `were passed to component but could not be automatically inherited ` +
158
+ `because component renders fragment or text root nodes.`
159
+ )
160
+ }
161
+ if ( eventAttrs . length ) {
162
+ warn (
163
+ `Extraneous non-emits event listeners (` +
164
+ ` ${ eventAttrs . join ( ', ' ) } ) ` +
165
+ `were passed to component but could not be automatically inherited ` +
166
+ `because component renders fragment or text root nodes. ` +
167
+ `If the listener is intended to be a component custom event listener only, ` +
168
+ `declare it using the "emits" option.`
169
+ )
170
+ }
163
171
}
164
172
}
165
173
}
@@ -246,7 +254,7 @@ const getChildRoot = (
246
254
return [ normalizeVNode ( childRoot ) , setRoot ]
247
255
}
248
256
249
- const getFallthroughAttrs = ( attrs : Data ) : Data | undefined => {
257
+ const getFunctionalFallthrough = ( attrs : Data ) : Data | undefined => {
250
258
let res : Data | undefined
251
259
for ( const key in attrs ) {
252
260
if ( key === 'class' || key === 'style' || isOn ( key ) ) {
@@ -256,6 +264,16 @@ const getFallthroughAttrs = (attrs: Data): Data | undefined => {
256
264
return res
257
265
}
258
266
267
+ const filterModelListeners = ( attrs : Data ) : Data => {
268
+ const res : Data = { }
269
+ for ( const key in attrs ) {
270
+ if ( ! isModelListener ( key ) ) {
271
+ res [ key ] = attrs [ key ]
272
+ }
273
+ }
274
+ return res
275
+ }
276
+
259
277
const isElementRoot = ( vnode : VNode ) => {
260
278
return (
261
279
vnode . shapeFlag & ShapeFlags . COMPONENT ||
0 commit comments