Skip to content

Commit 364f319

Browse files
committedNov 10, 2023
fix(hydration): force hydration for v-bind with .prop modifier
ref #7490
1 parent 34b5a5d commit 364f319

File tree

10 files changed

+51
-19
lines changed

10 files changed

+51
-19
lines changed
 

‎packages/compiler-core/__tests__/transforms/__snapshots__/vModel.spec.ts.snap

+1-1
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ return function render(_ctx, _cache) {
8585
return (_openBlock(), _createElementBlock(\\"input\\", {
8686
\\"foo-value\\": model,
8787
\\"onUpdate:fooValue\\": $event => ((model) = $event)
88-
}, null, 40 /* PROPS, HYDRATE_EVENTS */, [\\"foo-value\\", \\"onUpdate:fooValue\\"]))
88+
}, null, 40 /* PROPS, NEED_HYDRATION */, [\\"foo-value\\", \\"onUpdate:fooValue\\"]))
8989
}
9090
}"
9191
`;

‎packages/compiler-core/__tests__/transforms/transformElement.spec.ts

+16-4
Original file line numberDiff line numberDiff line change
@@ -1089,7 +1089,7 @@ describe('compiler: element transform', () => {
10891089
})
10901090
})
10911091

1092-
test('HYDRATE_EVENTS', () => {
1092+
test('NEED_HYDRATION for v-on', () => {
10931093
// ignore click events (has dedicated fast path)
10941094
const { node } = parseWithElementTransform(`<div @click="foo" />`, {
10951095
directiveTransforms: {
@@ -1108,12 +1108,24 @@ describe('compiler: element transform', () => {
11081108
}
11091109
)
11101110
expect(node2.patchFlag).toBe(
1111-
genFlagText([PatchFlags.PROPS, PatchFlags.HYDRATE_EVENTS])
1111+
genFlagText([PatchFlags.PROPS, PatchFlags.NEED_HYDRATION])
1112+
)
1113+
})
1114+
1115+
test('NEED_HYDRATION for v-bind.prop', () => {
1116+
const { node } = parseWithBind(`<div v-bind:id.prop="id" />`)
1117+
expect(node.patchFlag).toBe(
1118+
genFlagText([PatchFlags.PROPS, PatchFlags.NEED_HYDRATION])
1119+
)
1120+
1121+
const { node: node2 } = parseWithBind(`<div .id="id" />`)
1122+
expect(node2.patchFlag).toBe(
1123+
genFlagText([PatchFlags.PROPS, PatchFlags.NEED_HYDRATION])
11121124
)
11131125
})
11141126

11151127
// #5870
1116-
test('HYDRATE_EVENTS on dynamic component', () => {
1128+
test('NEED_HYDRATION on dynamic component', () => {
11171129
const { node } = parseWithElementTransform(
11181130
`<component :is="foo" @input="foo" />`,
11191131
{
@@ -1123,7 +1135,7 @@ describe('compiler: element transform', () => {
11231135
}
11241136
)
11251137
expect(node.patchFlag).toBe(
1126-
genFlagText([PatchFlags.PROPS, PatchFlags.HYDRATE_EVENTS])
1138+
genFlagText([PatchFlags.PROPS, PatchFlags.NEED_HYDRATION])
11271139
)
11281140
})
11291141
})

‎packages/compiler-core/src/transforms/transformElement.ts

+8-3
Original file line numberDiff line numberDiff line change
@@ -550,7 +550,7 @@ export function buildProps(
550550
)
551551
} else {
552552
// directives
553-
const { name, arg, exp, loc } = prop
553+
const { name, arg, exp, loc, modifiers } = prop
554554
const isVBind = name === 'bind'
555555
const isVOn = name === 'on'
556556

@@ -678,6 +678,11 @@ export function buildProps(
678678
continue
679679
}
680680

681+
// force hydration for v-bind with .prop modifier
682+
if (isVBind && modifiers.includes('prop')) {
683+
patchFlag |= PatchFlags.NEED_HYDRATION
684+
}
685+
681686
const directiveTransform = context.directiveTransforms[name]
682687
if (directiveTransform) {
683688
// has built-in directive transform.
@@ -743,12 +748,12 @@ export function buildProps(
743748
patchFlag |= PatchFlags.PROPS
744749
}
745750
if (hasHydrationEventBinding) {
746-
patchFlag |= PatchFlags.HYDRATE_EVENTS
751+
patchFlag |= PatchFlags.NEED_HYDRATION
747752
}
748753
}
749754
if (
750755
!shouldUseBlock &&
751-
(patchFlag === 0 || patchFlag === PatchFlags.HYDRATE_EVENTS) &&
756+
(patchFlag === 0 || patchFlag === PatchFlags.NEED_HYDRATION) &&
752757
(hasRef || hasVnodeHook || runtimeDirectives.length > 0)
753758
) {
754759
patchFlag |= PatchFlags.NEED_PATCH

‎packages/compiler-dom/__tests__/transforms/vOn.spec.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,7 @@ describe('compiler-dom: transform v-on', () => {
272272
// should not treat cached handler as dynamicProp, so it should have no
273273
// dynamicProps flags and only the hydration flag
274274
expect((root as any).children[0].codegenNode.patchFlag).toBe(
275-
genFlagText(PatchFlags.HYDRATE_EVENTS)
275+
genFlagText(PatchFlags.NEED_HYDRATION)
276276
)
277277
expect(prop).toMatchObject({
278278
key: {

‎packages/runtime-core/__tests__/hydration.spec.ts

+12
Original file line numberDiff line numberDiff line change
@@ -935,6 +935,18 @@ describe('SSR hydration', () => {
935935
)
936936
})
937937

938+
test('force hydrate prop with `.prop` modifier', () => {
939+
const { container } = mountWithHydration(
940+
'<input type="checkbox" :indeterminate.prop="true">',
941+
() =>
942+
h('input', {
943+
type: 'checkbox',
944+
'.indeterminate': true
945+
})
946+
)
947+
expect((container.firstChild! as any).indeterminate).toBe(true)
948+
})
949+
938950
test('force hydrate input v-model with non-string value bindings', () => {
939951
const { container } = mountWithHydration(
940952
'<input type="checkbox" value="true">',

‎packages/runtime-core/__tests__/vnode.spec.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -477,13 +477,13 @@ describe('vnode', () => {
477477
expect(vnode.dynamicChildren).toStrictEqual([vnode1])
478478
})
479479

480-
test('should not track vnodes with only HYDRATE_EVENTS flag', () => {
480+
test('should not track vnodes with only NEED_HYDRATION flag', () => {
481481
const hoist = createVNode('div')
482482
const vnode =
483483
(openBlock(),
484484
createBlock('div', null, [
485485
hoist,
486-
createVNode('div', null, 'text', PatchFlags.HYDRATE_EVENTS)
486+
createVNode('div', null, 'text', PatchFlags.NEED_HYDRATION)
487487
]))
488488
expect(vnode.dynamicChildren).toStrictEqual([])
489489
})

‎packages/runtime-core/src/hydration.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -334,13 +334,15 @@ export function createHydrationFunctions(
334334
if (
335335
forcePatch ||
336336
!optimized ||
337-
patchFlag & (PatchFlags.FULL_PROPS | PatchFlags.HYDRATE_EVENTS)
337+
patchFlag & (PatchFlags.FULL_PROPS | PatchFlags.NEED_HYDRATION)
338338
) {
339339
for (const key in props) {
340340
if (
341341
(forcePatch &&
342342
(key.endsWith('value') || key === 'indeterminate')) ||
343-
(isOn(key) && !isReservedProp(key))
343+
(isOn(key) && !isReservedProp(key)) ||
344+
// force hydrate v-bind with .prop modifiers
345+
key[0] === '.'
344346
) {
345347
patchProp(
346348
el,

‎packages/runtime-core/src/renderer.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -2395,7 +2395,7 @@ export function traverseStaticChildren(n1: VNode, n2: VNode, shallow = false) {
23952395
const c1 = ch1[i] as VNode
23962396
let c2 = ch2[i] as VNode
23972397
if (c2.shapeFlag & ShapeFlags.ELEMENT && !c2.dynamicChildren) {
2398-
if (c2.patchFlag <= 0 || c2.patchFlag === PatchFlags.HYDRATE_EVENTS) {
2398+
if (c2.patchFlag <= 0 || c2.patchFlag === PatchFlags.NEED_HYDRATION) {
23992399
c2 = ch2[i] = cloneIfMounted(ch2[i] as VNode)
24002400
c2.el = c1.el
24012401
}

‎packages/runtime-core/src/vnode.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -489,7 +489,7 @@ function createBaseVNode(
489489
(vnode.patchFlag > 0 || shapeFlag & ShapeFlags.COMPONENT) &&
490490
// the EVENTS flag is only for hydration and if it is the only flag, the
491491
// vnode should not be considered dynamic due to handler caching.
492-
vnode.patchFlag !== PatchFlags.HYDRATE_EVENTS
492+
vnode.patchFlag !== PatchFlags.NEED_HYDRATION
493493
) {
494494
currentBlock.push(vnode)
495495
}

‎packages/shared/src/patchFlags.ts

+5-4
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,11 @@ export const enum PatchFlags {
5757
FULL_PROPS = 1 << 4,
5858

5959
/**
60-
* Indicates an element with event listeners (which need to be attached
61-
* during hydration)
60+
* Indicates an element that requires props hydration
61+
* (but not necessarily patching)
62+
* e.g. event listeners & v-bind with prop modifier
6263
*/
63-
HYDRATE_EVENTS = 1 << 5,
64+
NEED_HYDRATION = 1 << 5,
6465

6566
/**
6667
* Indicates a fragment whose children order doesn't change.
@@ -131,7 +132,7 @@ export const PatchFlagNames: Record<PatchFlags, string> = {
131132
[PatchFlags.STYLE]: `STYLE`,
132133
[PatchFlags.PROPS]: `PROPS`,
133134
[PatchFlags.FULL_PROPS]: `FULL_PROPS`,
134-
[PatchFlags.HYDRATE_EVENTS]: `HYDRATE_EVENTS`,
135+
[PatchFlags.NEED_HYDRATION]: `NEED_HYDRATION`,
135136
[PatchFlags.STABLE_FRAGMENT]: `STABLE_FRAGMENT`,
136137
[PatchFlags.KEYED_FRAGMENT]: `KEYED_FRAGMENT`,
137138
[PatchFlags.UNKEYED_FRAGMENT]: `UNKEYED_FRAGMENT`,

0 commit comments

Comments
 (0)
Please sign in to comment.