diff --git a/packages/compiler-core/__tests__/transforms/__snapshots__/vModel.spec.ts.snap b/packages/compiler-core/__tests__/transforms/__snapshots__/vModel.spec.ts.snap
index e59df2d5458..983fe1223aa 100644
--- a/packages/compiler-core/__tests__/transforms/__snapshots__/vModel.spec.ts.snap
+++ b/packages/compiler-core/__tests__/transforms/__snapshots__/vModel.spec.ts.snap
@@ -85,7 +85,7 @@ return function render(_ctx, _cache) {
return (_openBlock(), _createElementBlock(\\"input\\", {
\\"foo-value\\": model,
\\"onUpdate:fooValue\\": $event => ((model) = $event)
- }, null, 40 /* PROPS, HYDRATE_EVENTS */, [\\"foo-value\\", \\"onUpdate:fooValue\\"]))
+ }, null, 40 /* PROPS, NEED_HYDRATION */, [\\"foo-value\\", \\"onUpdate:fooValue\\"]))
}
}"
`;
diff --git a/packages/compiler-core/__tests__/transforms/transformElement.spec.ts b/packages/compiler-core/__tests__/transforms/transformElement.spec.ts
index a1ae013a830..97559369d8a 100644
--- a/packages/compiler-core/__tests__/transforms/transformElement.spec.ts
+++ b/packages/compiler-core/__tests__/transforms/transformElement.spec.ts
@@ -1089,7 +1089,7 @@ describe('compiler: element transform', () => {
})
})
- test('HYDRATE_EVENTS', () => {
+ test('NEED_HYDRATION for v-on', () => {
// ignore click events (has dedicated fast path)
const { node } = parseWithElementTransform(`
`, {
directiveTransforms: {
@@ -1108,12 +1108,24 @@ describe('compiler: element transform', () => {
}
)
expect(node2.patchFlag).toBe(
- genFlagText([PatchFlags.PROPS, PatchFlags.HYDRATE_EVENTS])
+ genFlagText([PatchFlags.PROPS, PatchFlags.NEED_HYDRATION])
+ )
+ })
+
+ test('NEED_HYDRATION for v-bind.prop', () => {
+ const { node } = parseWithBind(``)
+ expect(node.patchFlag).toBe(
+ genFlagText([PatchFlags.PROPS, PatchFlags.NEED_HYDRATION])
+ )
+
+ const { node: node2 } = parseWithBind(``)
+ expect(node2.patchFlag).toBe(
+ genFlagText([PatchFlags.PROPS, PatchFlags.NEED_HYDRATION])
)
})
// #5870
- test('HYDRATE_EVENTS on dynamic component', () => {
+ test('NEED_HYDRATION on dynamic component', () => {
const { node } = parseWithElementTransform(
``,
{
@@ -1123,7 +1135,7 @@ describe('compiler: element transform', () => {
}
)
expect(node.patchFlag).toBe(
- genFlagText([PatchFlags.PROPS, PatchFlags.HYDRATE_EVENTS])
+ genFlagText([PatchFlags.PROPS, PatchFlags.NEED_HYDRATION])
)
})
})
diff --git a/packages/compiler-core/src/transforms/transformElement.ts b/packages/compiler-core/src/transforms/transformElement.ts
index 253b6be5efa..fd61f011051 100644
--- a/packages/compiler-core/src/transforms/transformElement.ts
+++ b/packages/compiler-core/src/transforms/transformElement.ts
@@ -550,7 +550,7 @@ export function buildProps(
)
} else {
// directives
- const { name, arg, exp, loc } = prop
+ const { name, arg, exp, loc, modifiers } = prop
const isVBind = name === 'bind'
const isVOn = name === 'on'
@@ -678,6 +678,11 @@ export function buildProps(
continue
}
+ // force hydration for v-bind with .prop modifier
+ if (isVBind && modifiers.includes('prop')) {
+ patchFlag |= PatchFlags.NEED_HYDRATION
+ }
+
const directiveTransform = context.directiveTransforms[name]
if (directiveTransform) {
// has built-in directive transform.
@@ -743,12 +748,12 @@ export function buildProps(
patchFlag |= PatchFlags.PROPS
}
if (hasHydrationEventBinding) {
- patchFlag |= PatchFlags.HYDRATE_EVENTS
+ patchFlag |= PatchFlags.NEED_HYDRATION
}
}
if (
!shouldUseBlock &&
- (patchFlag === 0 || patchFlag === PatchFlags.HYDRATE_EVENTS) &&
+ (patchFlag === 0 || patchFlag === PatchFlags.NEED_HYDRATION) &&
(hasRef || hasVnodeHook || runtimeDirectives.length > 0)
) {
patchFlag |= PatchFlags.NEED_PATCH
diff --git a/packages/compiler-dom/__tests__/transforms/vOn.spec.ts b/packages/compiler-dom/__tests__/transforms/vOn.spec.ts
index efc7fee374f..79ffcdef03c 100644
--- a/packages/compiler-dom/__tests__/transforms/vOn.spec.ts
+++ b/packages/compiler-dom/__tests__/transforms/vOn.spec.ts
@@ -272,7 +272,7 @@ describe('compiler-dom: transform v-on', () => {
// should not treat cached handler as dynamicProp, so it should have no
// dynamicProps flags and only the hydration flag
expect((root as any).children[0].codegenNode.patchFlag).toBe(
- genFlagText(PatchFlags.HYDRATE_EVENTS)
+ genFlagText(PatchFlags.NEED_HYDRATION)
)
expect(prop).toMatchObject({
key: {
diff --git a/packages/runtime-core/__tests__/hydration.spec.ts b/packages/runtime-core/__tests__/hydration.spec.ts
index e54960222c4..7ea607d3380 100644
--- a/packages/runtime-core/__tests__/hydration.spec.ts
+++ b/packages/runtime-core/__tests__/hydration.spec.ts
@@ -935,6 +935,18 @@ describe('SSR hydration', () => {
)
})
+ test('force hydrate prop with `.prop` modifier', () => {
+ const { container } = mountWithHydration(
+ '',
+ () =>
+ h('input', {
+ type: 'checkbox',
+ '.indeterminate': true
+ })
+ )
+ expect((container.firstChild! as any).indeterminate).toBe(true)
+ })
+
test('force hydrate input v-model with non-string value bindings', () => {
const { container } = mountWithHydration(
'',
diff --git a/packages/runtime-core/__tests__/vnode.spec.ts b/packages/runtime-core/__tests__/vnode.spec.ts
index 02774aafee4..11b2044a9e3 100644
--- a/packages/runtime-core/__tests__/vnode.spec.ts
+++ b/packages/runtime-core/__tests__/vnode.spec.ts
@@ -477,13 +477,13 @@ describe('vnode', () => {
expect(vnode.dynamicChildren).toStrictEqual([vnode1])
})
- test('should not track vnodes with only HYDRATE_EVENTS flag', () => {
+ test('should not track vnodes with only NEED_HYDRATION flag', () => {
const hoist = createVNode('div')
const vnode =
(openBlock(),
createBlock('div', null, [
hoist,
- createVNode('div', null, 'text', PatchFlags.HYDRATE_EVENTS)
+ createVNode('div', null, 'text', PatchFlags.NEED_HYDRATION)
]))
expect(vnode.dynamicChildren).toStrictEqual([])
})
diff --git a/packages/runtime-core/src/hydration.ts b/packages/runtime-core/src/hydration.ts
index 0e94495878e..516823c70c5 100644
--- a/packages/runtime-core/src/hydration.ts
+++ b/packages/runtime-core/src/hydration.ts
@@ -334,13 +334,15 @@ export function createHydrationFunctions(
if (
forcePatch ||
!optimized ||
- patchFlag & (PatchFlags.FULL_PROPS | PatchFlags.HYDRATE_EVENTS)
+ patchFlag & (PatchFlags.FULL_PROPS | PatchFlags.NEED_HYDRATION)
) {
for (const key in props) {
if (
(forcePatch &&
(key.endsWith('value') || key === 'indeterminate')) ||
- (isOn(key) && !isReservedProp(key))
+ (isOn(key) && !isReservedProp(key)) ||
+ // force hydrate v-bind with .prop modifiers
+ key[0] === '.'
) {
patchProp(
el,
diff --git a/packages/runtime-core/src/renderer.ts b/packages/runtime-core/src/renderer.ts
index 8799ecd473c..8a3b4ffa3af 100644
--- a/packages/runtime-core/src/renderer.ts
+++ b/packages/runtime-core/src/renderer.ts
@@ -2395,7 +2395,7 @@ export function traverseStaticChildren(n1: VNode, n2: VNode, shallow = false) {
const c1 = ch1[i] as VNode
let c2 = ch2[i] as VNode
if (c2.shapeFlag & ShapeFlags.ELEMENT && !c2.dynamicChildren) {
- if (c2.patchFlag <= 0 || c2.patchFlag === PatchFlags.HYDRATE_EVENTS) {
+ if (c2.patchFlag <= 0 || c2.patchFlag === PatchFlags.NEED_HYDRATION) {
c2 = ch2[i] = cloneIfMounted(ch2[i] as VNode)
c2.el = c1.el
}
diff --git a/packages/runtime-core/src/vnode.ts b/packages/runtime-core/src/vnode.ts
index 10ee03c29c6..04f1150e346 100644
--- a/packages/runtime-core/src/vnode.ts
+++ b/packages/runtime-core/src/vnode.ts
@@ -489,7 +489,7 @@ function createBaseVNode(
(vnode.patchFlag > 0 || shapeFlag & ShapeFlags.COMPONENT) &&
// the EVENTS flag is only for hydration and if it is the only flag, the
// vnode should not be considered dynamic due to handler caching.
- vnode.patchFlag !== PatchFlags.HYDRATE_EVENTS
+ vnode.patchFlag !== PatchFlags.NEED_HYDRATION
) {
currentBlock.push(vnode)
}
diff --git a/packages/shared/src/patchFlags.ts b/packages/shared/src/patchFlags.ts
index 58e8935aeb8..af5ee7b49e2 100644
--- a/packages/shared/src/patchFlags.ts
+++ b/packages/shared/src/patchFlags.ts
@@ -57,10 +57,11 @@ export const enum PatchFlags {
FULL_PROPS = 1 << 4,
/**
- * Indicates an element with event listeners (which need to be attached
- * during hydration)
+ * Indicates an element that requires props hydration
+ * (but not necessarily patching)
+ * e.g. event listeners & v-bind with prop modifier
*/
- HYDRATE_EVENTS = 1 << 5,
+ NEED_HYDRATION = 1 << 5,
/**
* Indicates a fragment whose children order doesn't change.
@@ -131,7 +132,7 @@ export const PatchFlagNames: Record = {
[PatchFlags.STYLE]: `STYLE`,
[PatchFlags.PROPS]: `PROPS`,
[PatchFlags.FULL_PROPS]: `FULL_PROPS`,
- [PatchFlags.HYDRATE_EVENTS]: `HYDRATE_EVENTS`,
+ [PatchFlags.NEED_HYDRATION]: `NEED_HYDRATION`,
[PatchFlags.STABLE_FRAGMENT]: `STABLE_FRAGMENT`,
[PatchFlags.KEYED_FRAGMENT]: `KEYED_FRAGMENT`,
[PatchFlags.UNKEYED_FRAGMENT]: `UNKEYED_FRAGMENT`,