@@ -729,6 +729,120 @@ describe('vModel', () => {
729
729
expect ( bar . checked ) . toEqual ( false )
730
730
} )
731
731
732
+ it ( 'should not update DOM unnecessarily' , async ( ) => {
733
+ const component = defineComponent ( {
734
+ data ( ) {
735
+ return { value : true }
736
+ } ,
737
+ render ( ) {
738
+ return [
739
+ withVModel (
740
+ h ( 'input' , {
741
+ type : 'checkbox' ,
742
+ 'onUpdate:modelValue' : setValue . bind ( this ) ,
743
+ } ) ,
744
+ this . value ,
745
+ ) ,
746
+ ]
747
+ } ,
748
+ } )
749
+ render ( h ( component ) , root )
750
+
751
+ const input = root . querySelector ( 'input' )
752
+ const data = root . _vnode . component . data
753
+
754
+ const setCheckedSpy = vi . spyOn ( input , 'checked' , 'set' )
755
+
756
+ // Trigger a change event without actually changing the value
757
+ triggerEvent ( 'change' , input )
758
+ await nextTick ( )
759
+ expect ( data . value ) . toEqual ( true )
760
+ expect ( setCheckedSpy ) . not . toHaveBeenCalled ( )
761
+
762
+ // Change the value and trigger a change event
763
+ input . checked = false
764
+ triggerEvent ( 'change' , input )
765
+ await nextTick ( )
766
+ expect ( data . value ) . toEqual ( false )
767
+ expect ( setCheckedSpy ) . toHaveBeenCalledTimes ( 1 )
768
+
769
+ setCheckedSpy . mockClear ( )
770
+
771
+ data . value = false
772
+ await nextTick ( )
773
+ expect ( input . checked ) . toEqual ( false )
774
+ expect ( setCheckedSpy ) . not . toHaveBeenCalled ( )
775
+
776
+ data . value = true
777
+ await nextTick ( )
778
+ expect ( input . checked ) . toEqual ( true )
779
+ expect ( setCheckedSpy ) . toHaveBeenCalledTimes ( 1 )
780
+ } )
781
+
782
+ it ( 'should handle array values correctly without unnecessary updates' , async ( ) => {
783
+ const component = defineComponent ( {
784
+ data ( ) {
785
+ return { value : [ 'foo' ] }
786
+ } ,
787
+ render ( ) {
788
+ return [
789
+ withVModel (
790
+ h ( 'input' , {
791
+ type : 'checkbox' ,
792
+ value : 'foo' ,
793
+ 'onUpdate:modelValue' : setValue . bind ( this ) ,
794
+ } ) ,
795
+ this . value ,
796
+ ) ,
797
+ withVModel (
798
+ h ( 'input' , {
799
+ type : 'checkbox' ,
800
+ value : 'bar' ,
801
+ 'onUpdate:modelValue' : setValue . bind ( this ) ,
802
+ } ) ,
803
+ this . value ,
804
+ ) ,
805
+ ]
806
+ } ,
807
+ } )
808
+ render ( h ( component ) , root )
809
+
810
+ const [ foo , bar ] = root . querySelectorAll ( 'input' )
811
+ const data = root . _vnode . component . data
812
+
813
+ const setCheckedSpyFoo = vi . spyOn ( foo , 'checked' , 'set' )
814
+ const setCheckedSpyBar = vi . spyOn ( bar , 'checked' , 'set' )
815
+
816
+ expect ( foo . checked ) . toEqual ( true )
817
+ expect ( bar . checked ) . toEqual ( false )
818
+
819
+ triggerEvent ( 'change' , foo )
820
+ await nextTick ( )
821
+ expect ( data . value ) . toEqual ( [ 'foo' ] )
822
+ expect ( setCheckedSpyFoo ) . not . toHaveBeenCalled ( )
823
+
824
+ bar . checked = true
825
+ triggerEvent ( 'change' , bar )
826
+ await nextTick ( )
827
+ expect ( data . value ) . toEqual ( [ 'foo' , 'bar' ] )
828
+ expect ( setCheckedSpyBar ) . toHaveBeenCalledTimes ( 1 )
829
+
830
+ setCheckedSpyFoo . mockClear ( )
831
+ setCheckedSpyBar . mockClear ( )
832
+
833
+ data . value = [ 'foo' , 'bar' ]
834
+ await nextTick ( )
835
+ expect ( setCheckedSpyFoo ) . not . toHaveBeenCalled ( )
836
+ expect ( setCheckedSpyBar ) . not . toHaveBeenCalled ( )
837
+
838
+ data . value = [ 'bar' ]
839
+ await nextTick ( )
840
+ expect ( setCheckedSpyFoo ) . toHaveBeenCalledTimes ( 1 )
841
+ expect ( setCheckedSpyBar ) . not . toHaveBeenCalled ( )
842
+ expect ( foo . checked ) . toEqual ( false )
843
+ expect ( bar . checked ) . toEqual ( true )
844
+ } )
845
+
732
846
it ( 'should work with radio' , async ( ) => {
733
847
const component = defineComponent ( {
734
848
data ( ) {
0 commit comments