import { ComponentInternalInstance , FunctionalComponent , Data , getComponentName } from './component' ; import { VNode , normalizeVNode , createVNode , Comment , cloneVNode , VNodeArrayChildren , isVNode , blockStack } from './vnode' ; import { handleError , ErrorCodes } from './errorHandling' ; import { PatchFlags , ShapeFlags , isOn , isModelListener } from '@vue/shared' ; import { warn } from './warning' ; import { isHmrUpdating } from './hmr' ; import { NormalizedProps } from './componentProps' ; import { isEmitListener } from './componentEmits' ; import { setCurrentRenderingInstance } from './componentRenderContext' ; import { DeprecationTypes , isCompatEnabled , warnDeprecation } from './compat/compatConfig' ; /**
 * dev only flag to track whether $attrs was used during render.
 * If $attrs was used during render then the warning for failed attrs
 * fallthrough can be suppressed.
 */ let accessedAttrs = false ; export function markAttrsAccessed ( ) { accessedAttrs = true ; } export function renderComponentRoot ( instance ) { const { type : Component , vnode , proxy , withProxy , props , propsOptions : [ propsOptions ] , slots , attrs , emit , render , renderCache , data , setupState , ctx , inheritAttrs } = instance ; let result ; const prev = setCurrentRenderingInstance ( instance ) ; if ( __DEV__ ) { accessedAttrs = false ; } try { let fallthroughAttrs ; if ( vnode . shapeFlag & ShapeFlags . STATEFUL_COMPONENT ) { const proxyToUse = withProxy || proxy ; result = normalizeVNode ( render ! . call ( proxyToUse , proxyToUse ! , renderCache , props , setupState , data , ctx ) ) ; fallthroughAttrs = attrs ; } else { const render = Component as FunctionalComponent ; if ( __DEV__ && attrs === props ) { markAttrsAccessed ( ) ; } result = normalizeVNode ( render . length > 1 ? render ( props , __DEV__ ? { get attrs ( ) { markAttrsAccessed ( ) ; return attrs ; } , slots , emit } : { attrs , slots , emit } ) : render ( props , null as any ) ) ; fallthroughAttrs = Component . props ? attrs : getFunctionalFallthrough ( attrs ) ; } let root = result ; let setRoot = undefined ; if ( __DEV__ && result . patchFlag > 0 && result . patchFlag & PatchFlags . DEV_ROOT_FRAGMENT ) { ; [ root , setRoot ] = getChildRoot ( result ) ; } if ( fallthroughAttrs && inheritAttrs !== false ) { const keys = Object . keys ( fallthroughAttrs ) ; const { shapeFlag } = root ; if ( keys . length ) { if ( shapeFlag & ( ShapeFlags . ELEMENT | ShapeFlags . COMPONENT ) ) { if ( propsOptions && keys . some ( isModelListener ) ) { fallthroughAttrs = filterModelListeners ( fallthroughAttrs , propsOptions ) ; } root = cloneVNode ( root , fallthroughAttrs ) ; } else if ( __DEV__ && ! accessedAttrs && root . type !== Comment ) { const allAttrs = Object . keys ( attrs ) ; const eventAttrs = [ ] ; const extraAttrs = [ ] ; for ( let i = 0 , l = allAttrs . length ; i < l ; i ++ ) { const key = allAttrs [ i ] ; if ( isOn ( key ) ) { if ( ! isModelListener ( key ) ) { eventAttrs . push ( key [ 2 ] . toLowerCase ( ) + key . slice ( 3 ) ) ; } } else { extraAttrs . push ( key ) ; } } if ( extraAttrs . length ) { warn ( `Extraneous non-props attributes (` + `${ extraAttrs . join ( ', ' ) }) ` + `were passed to component but could not be automatically inherited ` + `because component renders fragment or text root nodes.` ) ; } if ( eventAttrs . length ) { warn ( `Extraneous non-emits event listeners (` + `${ eventAttrs . join ( ', ' ) }) ` + `were passed to component but could not be automatically inherited ` + `because component renders fragment or text root nodes. ` + `If the listener is intended to be a component custom event listener only, ` + `declare it using the "emits" option.` ) ; } } } } if ( __COMPAT__ && isCompatEnabled ( DeprecationTypes . INSTANCE_ATTRS_CLASS_STYLE , instance ) && vnode . shapeFlag & ShapeFlags . STATEFUL_COMPONENT && root . shapeFlag & ( ShapeFlags . ELEMENT | ShapeFlags . COMPONENT ) ) { const { class : cls , style } = vnode . props || { } ; if ( cls || style ) { if ( __DEV__ && inheritAttrs === false ) { warnDeprecation ( DeprecationTypes . INSTANCE_ATTRS_CLASS_STYLE , instance , getComponentName ( instance . type ) ) ; } root = cloneVNode ( root , { class : cls , style : style } ) ; } } if ( vnode . dirs ) { if ( __DEV__ && ! isElementRoot ( root ) ) { warn ( `Runtime directive used on component with non-element root node. ` + `The directives will not function as intended.` ) ; } root . dirs = root . dirs ? root . dirs . concat ( vnode . dirs ) : vnode . dirs ; } if ( vnode . transition ) { if ( __DEV__ && ! isElementRoot ( root ) ) { warn ( `Component inside <Transition> renders non-element root node ` + `that cannot be animated.` ) ; } root . transition = vnode . transition ; } if ( __DEV__ && setRoot ) { setRoot ( root ) ; } else { result = root ; } } catch ( err ) { blockStack . length = 0 ; handleError ( err , instance , ErrorCodes . RENDER_FUNCTION ) ; result = createVNode ( Comment ) ; } setCurrentRenderingInstance ( prev ) ; return result ; } /**
 * dev only
 * In dev mode, template root level comments are rendered, which turns the
 * template into a fragment root, but we need to locate the single element
 * root for attrs and scope id processing.
 */ const getChildRoot = ( vnode ) : [ VNode , ( ( root : VNode ) => void ) | undefined ] => { const rawChildren = vnode . children as VNodeArrayChildren ; const dynamicChildren = vnode . dynamicChildren ; const childRoot = filterSingleRoot ( rawChildren ) ; if ( ! childRoot ) { return [ vnode , undefined ] ; } const index = rawChildren . indexOf ( childRoot ) ; const dynamicIndex = dynamicChildren ? dynamicChildren . indexOf ( childRoot ) : - 1 ; const setRoot = ( updatedRoot ) => { rawChildren [ index ] = updatedRoot ; if ( dynamicChildren ) { if ( dynamicIndex > - 1 ) { dynamicChildren [ dynamicIndex ] = updatedRoot ; } else if ( updatedRoot . patchFlag > 0 ) { vnode . dynamicChildren = [ ... dynamicChildren , updatedRoot ] ; } } } ; return [ normalizeVNode ( childRoot ) , setRoot ] ; } ; export function filterSingleRoot ( children ) { let singleRoot ; for ( let i = 0 ; i < children . length ; i ++ ) { const child = children [ i ] ; if ( isVNode ( child ) ) { if ( child . type !== Comment || child . children === 'v-if' ) { if ( singleRoot ) { return ; } else { singleRoot = child ; } } } else { return ; } } return singleRoot ; } const getFunctionalFallthrough = ( attrs ) : Data | undefined => { let res ; for ( const key in attrs ) { if ( key === 'class' || key === 'style' || isOn ( key ) ) { ; ( res || ( res = { } ) ) [ key ] = attrs [ key ] ; } } return res ; } ; const filterModelListeners = ( attrs , props ) : Data => { const res = { } ; for ( const key in attrs ) { if ( ! isModelListener ( key ) || ! ( key . slice ( 9 ) in props ) ) { res [ key ] = attrs [ key ] ; } } return res ; } ; const isElementRoot = ( vnode ) => { return ( vnode . shapeFlag & ( ShapeFlags . COMPONENT | ShapeFlags . ELEMENT ) || vnode . type === Comment ) ; } ; export function shouldUpdateComponent ( prevVNode , nextVNode , optimized ? ) { const { props : prevProps , children : prevChildren , component } = prevVNode ; const { props : nextProps , children : nextChildren , patchFlag } = nextVNode ; const emits = component ! . emitsOptions ; if ( __DEV__ && ( prevChildren || nextChildren ) && isHmrUpdating ) { return true ; } if ( nextVNode . dirs || nextVNode . transition ) { return true ; } if ( optimized && patchFlag >= 0 ) { if ( patchFlag & PatchFlags . DYNAMIC_SLOTS ) { return true ; } if ( patchFlag & PatchFlags . FULL_PROPS ) { if ( ! prevProps ) { return ! ! nextProps ; } return hasPropsChanged ( prevProps , nextProps ! , emits ) ; } else if ( patchFlag & PatchFlags . PROPS ) { const dynamicProps = nextVNode . dynamicProps ! ; for ( let i = 0 ; i < dynamicProps . length ; i ++ ) { const key = dynamicProps [ i ] ; if ( nextProps ! [ key ] !== prevProps ! [ key ] && ! isEmitListener ( emits , key ) ) { return true ; } } } } else { if ( prevChildren || nextChildren ) { if ( ! nextChildren || ! ( nextChildren as any ) . $stable ) { return true ; } } if ( prevProps === nextProps ) { return false ; } if ( ! prevProps ) { return ! ! nextProps ; } if ( ! nextProps ) { return true ; } return hasPropsChanged ( prevProps , nextProps , emits ) ; } return false ; } function hasPropsChanged ( prevProps , nextProps , emitsOptions ) { const nextKeys = Object . keys ( nextProps ) ; if ( nextKeys . length !== Object . keys ( prevProps ) . length ) { return true ; } for ( let i = 0 ; i < nextKeys . length ; i ++ ) { const key = nextKeys [ i ] ; if ( nextProps [ key ] !== prevProps [ key ] && ! isEmitListener ( emitsOptions , key ) ) { return true ; } } return false ; } export function updateHOCHostEl ( { vnode , parent } , el ) { while ( parent && parent . subTree === vnode ) { ; ( vnode = parent . vnode ) . el = el ; parent = parent . parent ; } }