diff --git a/packages/runtime-core/__tests__/hydration.spec.ts b/packages/runtime-core/__tests__/hydration.spec.ts
index c55a9ab6104..ffe54882b52 100644
--- a/packages/runtime-core/__tests__/hydration.spec.ts
+++ b/packages/runtime-core/__tests__/hydration.spec.ts
@@ -10,9 +10,13 @@ import {
onMounted,
defineAsyncComponent,
defineComponent,
- createTextVNode
+ createTextVNode,
+ createVNode,
+ withDirectives,
+ vModelCheckbox
} from '@vue/runtime-dom'
import { renderToString, SSRContext } from '@vue/server-renderer'
+import { PatchFlags } from '../../shared/src'
function mountWithHydration(html: string, render: () => any) {
const container = document.createElement('div')
@@ -761,6 +765,36 @@ describe('SSR hydration', () => {
)
})
+ test('force hydrate input v-model with non-string value bindings', () => {
+ const { container } = mountWithHydration(
+ '',
+ () =>
+ withDirectives(
+ createVNode(
+ 'input',
+ { type: 'checkbox', 'true-value': true },
+ null,
+ PatchFlags.PROPS,
+ ['true-value']
+ ),
+ [[vModelCheckbox, true]]
+ )
+ )
+ expect((container.firstChild as any)._trueValue).toBe(true)
+ })
+
+ test('force hydrate select option with non-string value bindings', () => {
+ const { container } = mountWithHydration(
+ '',
+ () =>
+ h('select', [
+ // hoisted because bound value is a constant...
+ createVNode('option', { value: true }, null, -1 /* HOISTED */)
+ ])
+ )
+ expect((container.firstChild!.firstChild as any)._value).toBe(true)
+ })
+
describe('mismatch handling', () => {
test('text node', () => {
const { container } = mountWithHydration(`foo`, () => 'bar')
diff --git a/packages/runtime-core/src/hydration.ts b/packages/runtime-core/src/hydration.ts
index f4f7ea4795a..94c221db195 100644
--- a/packages/runtime-core/src/hydration.ts
+++ b/packages/runtime-core/src/hydration.ts
@@ -264,21 +264,28 @@ export function createHydrationFunctions(
optimized: boolean
) => {
optimized = optimized || !!vnode.dynamicChildren
- const { props, patchFlag, shapeFlag, dirs } = vnode
+ const { type, props, patchFlag, shapeFlag, dirs } = vnode
+ // #4006 for form elements with non-string v-model value bindings
+ // e.g.