]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(runtime-dom): prevent unnecessary updates in v-model checkbox when value is uncha...
authorTycho <jh.leong@outlook.com>
Fri, 11 Oct 2024 13:00:08 +0000 (21:00 +0800)
committerGitHub <noreply@github.com>
Fri, 11 Oct 2024 13:00:08 +0000 (21:00 +0800)
close #12144

packages/runtime-dom/src/directives/vModel.ts
packages/vue/__tests__/e2e/vModel.spec.ts [new file with mode: 0644]

index 5a27b245a66fb5ab90fa6bd325e91d3ed8a0080c..5057e16d472bc59304b2bb386e96b53934e6cf92 100644 (file)
@@ -160,7 +160,7 @@ export const vModelCheckbox: ModelDirective<HTMLInputElement> = {
 
 function setChecked(
   el: HTMLInputElement,
-  { value }: DirectiveBinding,
+  { value, oldValue }: DirectiveBinding,
   vnode: VNode,
 ) {
   // store the v-model value on the element so it can be accessed by the
@@ -173,6 +173,7 @@ function setChecked(
   } else if (isSet(value)) {
     checked = value.has(vnode.props!.value)
   } else {
+    if (value === oldValue) return
     checked = looseEqual(value, getCheckboxValue(el, true))
   }
 
diff --git a/packages/vue/__tests__/e2e/vModel.spec.ts b/packages/vue/__tests__/e2e/vModel.spec.ts
new file mode 100644 (file)
index 0000000..e1a06bd
--- /dev/null
@@ -0,0 +1,57 @@
+import path from 'node:path'
+import { setupPuppeteer } from './e2eUtils'
+
+const { page, click, isChecked } = setupPuppeteer()
+import { nextTick } from 'vue'
+
+beforeEach(async () => {
+  await page().addScriptTag({
+    path: path.resolve(__dirname, '../../dist/vue.global.js'),
+  })
+  await page().setContent(`<div id="app"></div>`)
+})
+
+// #12144
+test('checkbox click with v-model', async () => {
+  await page().evaluate(() => {
+    const { createApp } = (window as any).Vue
+    createApp({
+      template: `
+      <label>
+        <input 
+          id="first"
+          type="checkbox"
+          v-model="first"/>
+        First
+      </label>
+      <br>  
+      <label>
+        <input
+          id="second"
+          type="checkbox"
+          v-model="second"      
+          @click="secondClick"/>    
+          Second
+      </label> 
+        `,
+      data() {
+        return {
+          first: true,
+          second: false,
+        }
+      },
+      methods: {
+        secondClick(this: any) {
+          this.first = false
+        },
+      },
+    }).mount('#app')
+  })
+
+  expect(await isChecked('#first')).toBe(true)
+  expect(await isChecked('#second')).toBe(false)
+  await click('#second')
+  await nextTick()
+  expect(await isChecked('#first')).toBe(false)
+  expect(await isChecked('#second')).toBe(true)
+})