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

fix(defineModel): support local mutation when only prop but no listener is passed

Evan You 2 лет назад
Родитель
Сommit
97ce041910

+ 35 - 0
packages/runtime-core/__tests__/apiSetupHelpers.spec.ts

@@ -279,6 +279,41 @@ describe('SFC <script setup> helpers', () => {
       expect(serializeInner(root)).toBe('bar')
     })
 
+    test('without parent listener (local mutation)', async () => {
+      let foo: any
+      const update = () => {
+        foo.value = 'bar'
+      }
+
+      const compRender = vi.fn()
+      const Comp = defineComponent({
+        props: ['foo'],
+        emits: ['update:foo'],
+        setup(props) {
+          foo = useModel(props, 'foo')
+          return () => {
+            compRender()
+            return foo.value
+          }
+        },
+      })
+
+      const root = nodeOps.createElement('div')
+      // provide initial value
+      render(h(Comp, { foo: 'initial' }), root)
+      expect(compRender).toBeCalledTimes(1)
+      expect(serializeInner(root)).toBe('initial')
+
+      expect(foo.value).toBe('initial')
+      update()
+      // when parent didn't provide value, local mutation is enabled
+      expect(foo.value).toBe('bar')
+
+      await nextTick()
+      expect(compRender).toBeCalledTimes(2)
+      expect(serializeInner(root)).toBe('bar')
+    })
+
     test('default value', async () => {
       let count: any
       const inc = () => {

+ 13 - 1
packages/runtime-core/src/apiSetupHelpers.ts

@@ -3,6 +3,7 @@ import {
   type LooseRequired,
   type Prettify,
   type UnionToIntersection,
+  camelize,
   extend,
   hasChanged,
   isArray,
@@ -380,6 +381,8 @@ export function useModel(
     return ref() as any
   }
 
+  const camelizedName = camelize(name)
+
   const res = customRef((track, trigger) => {
     let localValue: any
     watchSyncEffect(() => {
@@ -396,7 +399,16 @@ export function useModel(
       },
       set(value) {
         const rawProps = i.vnode!.props
-        if (!(rawProps && name in rawProps) && hasChanged(value, localValue)) {
+        if (
+          !(
+            rawProps &&
+            // check if parent has passed v-model
+            (name in rawProps || camelizedName in rawProps) &&
+            (`onUpdate:${name}` in rawProps ||
+              `onUpdate:${camelizedName}` in rawProps)
+          ) &&
+          hasChanged(value, localValue)
+        ) {
           localValue = value
           trigger()
         }