Просмотр исходного кода

fix(defineModel): detect changes respect custom getter and setter (#11543)

fix: #11541
fix: #11526
close: #11527
LiuSeen 1 год назад
Родитель
Сommit
e0428884b5

+ 92 - 0
packages/runtime-core/__tests__/helpers/useModel.spec.ts

@@ -657,4 +657,96 @@ describe('useModel', () => {
     expect(setValue).toBeCalledTimes(2)
     expect(msg.value).toBe(defaultVal)
   })
+
+  // #11526
+  test('custom getter', () => {
+    let changeChildMsg!: (val: boolean) => void
+    const getter = (value: boolean) => !value
+
+    const Comp = defineComponent({
+      props: ['msg'],
+      emits: ['update:msg'],
+      setup(props) {
+        const childMsg = useModel(props, 'msg', {
+          get: getter,
+          set: value => !value,
+        })
+        changeChildMsg = (val: boolean) => (childMsg.value = val)
+        return () => {
+          return childMsg.value
+        }
+      },
+    })
+
+    const defaultVal = false
+    const msg = ref(defaultVal)
+    const Parent = defineComponent({
+      setup() {
+        return () =>
+          h(Comp, {
+            msg: msg.value,
+            'onUpdate:msg': val => {
+              msg.value = val
+            },
+          })
+      },
+    })
+
+    const root = nodeOps.createElement('div')
+    render(h(Parent), root)
+
+    changeChildMsg(!getter(msg.value))
+    expect(msg.value).toBe(true)
+
+    changeChildMsg(!getter(msg.value))
+    expect(msg.value).toBe(false)
+  })
+
+  // #11541
+  test('custom setter', () => {
+    let changeChildMsg!: (val: boolean) => void
+
+    const Comp = defineComponent({
+      props: ['msg'],
+      emits: ['update:msg'],
+      setup(props) {
+        const childMsg = useModel(props, 'msg', {
+          set: value => {
+            if (value === msg.value) {
+              return null
+            } else {
+              return value
+            }
+          },
+        })
+        changeChildMsg = (val: boolean) => (childMsg.value = val)
+        return () => {
+          return childMsg.value
+        }
+      },
+    })
+
+    const defaultVal = false
+    const msg = ref(defaultVal)
+    const Parent = defineComponent({
+      setup() {
+        return () =>
+          h(Comp, {
+            msg: msg.value,
+            'onUpdate:msg': val => {
+              msg.value = val
+            },
+          })
+      },
+    })
+
+    const root = nodeOps.createElement('div')
+    render(h(Parent), root)
+
+    changeChildMsg(true)
+    expect(msg.value).toBe(true)
+
+    changeChildMsg(true)
+    expect(msg.value).toBe(null)
+  })
 })

+ 3 - 2
packages/runtime-core/src/helpers/useModel.ts

@@ -51,8 +51,9 @@ export function useModel(
       },
 
       set(value) {
+        const emittedValue = options.set ? options.set(value) : value
         if (
-          !hasChanged(value, localValue) &&
+          !hasChanged(emittedValue, localValue) &&
           !(prevSetValue !== EMPTY_OBJ && hasChanged(value, prevSetValue))
         ) {
           return
@@ -74,7 +75,7 @@ export function useModel(
           localValue = value
           trigger()
         }
-        const emittedValue = options.set ? options.set(value) : value
+
         i.emit(`update:${name}`, emittedValue)
         // #10279: if the local value is converted via a setter but the value
         // emitted to parent was the same, the parent will not trigger any