@@ -448,7 +448,7 @@ export function createHydrationFunctions(
448448 ) {
449449 for ( const key in props ) {
450450 // check hydration mismatch
451- if ( __DEV__ && propHasMismatch ( el , key , props [ key ] ) ) {
451+ if ( __DEV__ && propHasMismatch ( el , key , props [ key ] , vnode ) ) {
452452 hasMismatch = true
453453 }
454454 if (
@@ -712,7 +712,12 @@ export function createHydrationFunctions(
712712/**
713713 * Dev only
714714 */
715- function propHasMismatch ( el : Element , key : string , clientValue : any ) : boolean {
715+ function propHasMismatch (
716+ el : Element ,
717+ key : string ,
718+ clientValue : any ,
719+ vnode : VNode ,
720+ ) : boolean {
716721 let mismatchType : string | undefined
717722 let mismatchKey : string | undefined
718723 let actual : any
@@ -726,24 +731,41 @@ function propHasMismatch(el: Element, key: string, clientValue: any): boolean {
726731 mismatchType = mismatchKey = `class`
727732 }
728733 } else if ( key === 'style' ) {
729- actual = el . getAttribute ( 'style' )
730- expected = isString ( clientValue )
731- ? clientValue
732- : stringifyStyle ( normalizeStyle ( clientValue ) )
733- if ( actual !== expected ) {
734+ // style might be in different order, but that doesn't affect cascade
735+ actual = toStyleMap ( el . getAttribute ( 'style' ) || '' )
736+ expected = toStyleMap (
737+ isString ( clientValue )
738+ ? clientValue
739+ : stringifyStyle ( normalizeStyle ( clientValue ) ) ,
740+ )
741+ // If `v-show=false`, `display: 'none'` should be added to expected
742+ if ( vnode . dirs ) {
743+ for ( const { dir, value } of vnode . dirs ) {
744+ // @ts -expect-error only vShow has this internal name
745+ if ( dir . name === 'show' && ! value ) {
746+ expected . set ( 'display' , 'none' )
747+ }
748+ }
749+ }
750+ if ( ! isMapEqual ( actual , expected ) ) {
734751 mismatchType = mismatchKey = 'style'
735752 }
736753 } else if (
737754 ( el instanceof SVGElement && isKnownSvgAttr ( key ) ) ||
738755 ( el instanceof HTMLElement && ( isBooleanAttr ( key ) || isKnownHtmlAttr ( key ) ) )
739756 ) {
740- actual = el . hasAttribute ( key ) && el . getAttribute ( key )
757+ // #10000 some attrs such as textarea.value can't be get by `hasAttribute`
758+ actual = el . hasAttribute ( key )
759+ ? el . getAttribute ( key )
760+ : key in el
761+ ? el [ key as keyof typeof el ]
762+ : ''
741763 expected = isBooleanAttr ( key )
742764 ? includeBooleanAttr ( clientValue )
743765 ? ''
744766 : false
745767 : clientValue == null
746- ? false
768+ ? ''
747769 : String ( clientValue )
748770 if ( actual !== expected ) {
749771 mismatchType = `attribute`
@@ -783,3 +805,28 @@ function isSetEqual(a: Set<string>, b: Set<string>): boolean {
783805 }
784806 return true
785807}
808+
809+ function toStyleMap ( str : string ) : Map < string , string > {
810+ const styleMap : Map < string , string > = new Map ( )
811+ for ( const item of str . split ( ';' ) ) {
812+ let [ key , value ] = item . split ( ':' )
813+ key = key ?. trim ( )
814+ value = value ?. trim ( )
815+ if ( key && value ) {
816+ styleMap . set ( key , value )
817+ }
818+ }
819+ return styleMap
820+ }
821+
822+ function isMapEqual ( a : Map < string , string > , b : Map < string , string > ) : boolean {
823+ if ( a . size !== b . size ) {
824+ return false
825+ }
826+ for ( const [ key , value ] of a ) {
827+ if ( value !== b . get ( key ) ) {
828+ return false
829+ }
830+ }
831+ return true
832+ }
0 commit comments