@@ -448,7 +448,7 @@ export function createHydrationFunctions(
448
448
) {
449
449
for ( const key in props ) {
450
450
// check hydration mismatch
451
- if ( __DEV__ && propHasMismatch ( el , key , props [ key ] ) ) {
451
+ if ( __DEV__ && propHasMismatch ( el , key , props [ key ] , vnode ) ) {
452
452
hasMismatch = true
453
453
}
454
454
if (
@@ -712,7 +712,12 @@ export function createHydrationFunctions(
712
712
/**
713
713
* Dev only
714
714
*/
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 {
716
721
let mismatchType : string | undefined
717
722
let mismatchKey : string | undefined
718
723
let actual : any
@@ -726,24 +731,41 @@ function propHasMismatch(el: Element, key: string, clientValue: any): boolean {
726
731
mismatchType = mismatchKey = `class`
727
732
}
728
733
} 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 ) ) {
734
751
mismatchType = mismatchKey = 'style'
735
752
}
736
753
} else if (
737
754
( el instanceof SVGElement && isKnownSvgAttr ( key ) ) ||
738
755
( el instanceof HTMLElement && ( isBooleanAttr ( key ) || isKnownHtmlAttr ( key ) ) )
739
756
) {
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
+ : ''
741
763
expected = isBooleanAttr ( key )
742
764
? includeBooleanAttr ( clientValue )
743
765
? ''
744
766
: false
745
767
: clientValue == null
746
- ? false
768
+ ? ''
747
769
: String ( clientValue )
748
770
if ( actual !== expected ) {
749
771
mismatchType = `attribute`
@@ -783,3 +805,28 @@ function isSetEqual(a: Set<string>, b: Set<string>): boolean {
783
805
}
784
806
return true
785
807
}
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