|
|
@@ -134,96 +134,7 @@ export const createFor = (
|
|
|
if (!isMounted) {
|
|
|
isMounted = true
|
|
|
if (isHydrating) {
|
|
|
- const hydrationStart = currentHydrationNode!
|
|
|
- let exitHydrationBoundary: (() => void) | undefined
|
|
|
- let nextNode
|
|
|
- const emptyLocalRange =
|
|
|
- isComment(hydrationStart, ']') &&
|
|
|
- isComment(hydrationStart.previousSibling!, '[')
|
|
|
- const slotEndAnchor = getCurrentSlotEndAnchor()
|
|
|
- const slotFallbackRange =
|
|
|
- isHydratingSlotFallbackActive() && slotEndAnchor
|
|
|
-
|
|
|
- try {
|
|
|
- if (emptyLocalRange && newLength) {
|
|
|
- parentAnchor = markHydrationAnchor(hydrationStart)
|
|
|
- exitHydrationBoundary = enterHydrationBoundary(parentAnchor)
|
|
|
- for (let i = 0; i < newLength; i++) {
|
|
|
- mount(source, i)
|
|
|
- }
|
|
|
- setCurrentHydrationNode(parentAnchor)
|
|
|
- } else {
|
|
|
- for (let i = 0; i < newLength; i++) {
|
|
|
- if (isComment(currentHydrationNode!, ']')) {
|
|
|
- nextNode = markHydrationAnchor(currentHydrationNode!)
|
|
|
- setCurrentHydrationNode(nextNode)
|
|
|
- } else {
|
|
|
- nextNode = locateNextNode(currentHydrationNode!)
|
|
|
- }
|
|
|
- mount(source, i)
|
|
|
- if (nextNode) setCurrentHydrationNode(nextNode)
|
|
|
- }
|
|
|
-
|
|
|
- // special handling transition-group + v-for, without <!--]--> marker
|
|
|
- const resolvedAnchor =
|
|
|
- resolveForHydrationAnchor &&
|
|
|
- resolveForHydrationAnchor(
|
|
|
- hydrationStart,
|
|
|
- newLength ? nextNode : currentHydrationNode,
|
|
|
- )
|
|
|
- if (resolvedAnchor) {
|
|
|
- parentAnchor = resolvedAnchor
|
|
|
- pendingHydrationAnchor = true
|
|
|
- } else if (slotFallbackRange && !isValidBlock(newBlocks)) {
|
|
|
- // Slot fallback can fall through an empty/invalid `v-for`. In that
|
|
|
- // case SSR only rendered the parent slot range, so this `v-for` has no
|
|
|
- // own `<!--]-->` to reuse. If `hydrationStart` is not the parent slot
|
|
|
- // end anchor, use `hydrationStart.nextSibling` as the insertion point
|
|
|
- // so the runtime `<!--for-->` lands immediately after that local SSR
|
|
|
- // range. Otherwise insert it before the parent slot end anchor.
|
|
|
- const anchor =
|
|
|
- // The invalid list still consumed local SSR item ranges.
|
|
|
- currentHydrationNode !== hydrationStart
|
|
|
- ? currentHydrationNode!
|
|
|
- : // Empty source with trailing slot siblings.
|
|
|
- hydrationStart !== slotEndAnchor
|
|
|
- ? hydrationStart.nextSibling!
|
|
|
- : slotEndAnchor!
|
|
|
- parentAnchor = markHydrationAnchor(
|
|
|
- __DEV__ ? createComment('for') : createTextNode(),
|
|
|
- )
|
|
|
- pendingHydrationAnchor = true
|
|
|
- if (
|
|
|
- currentHydrationNode === hydrationStart ||
|
|
|
- currentHydrationNode === slotEndAnchor
|
|
|
- ) {
|
|
|
- setCurrentHydrationNode(hydrationStart)
|
|
|
- }
|
|
|
- queuePostFlushCb(() => {
|
|
|
- const parentNode = anchor.parentNode
|
|
|
- if (parentNode) parentNode.insertBefore(parentAnchor, anchor)
|
|
|
- })
|
|
|
- } else {
|
|
|
- const close = locateHydrationBoundaryClose(currentHydrationNode!)
|
|
|
- parentAnchor = markHydrationAnchor(close)
|
|
|
- exitHydrationBoundary = enterHydrationBoundary(parentAnchor)
|
|
|
- if (__DEV__ && !isComment(parentAnchor, ']')) {
|
|
|
- throw new Error(
|
|
|
- `v-for fragment anchor node was not found. this is likely a Vue internal bug.`,
|
|
|
- )
|
|
|
- }
|
|
|
-
|
|
|
- // optimization: cache the fragment end anchor as $llc (last logical child)
|
|
|
- // so that locateChildByLogicalIndex can skip the entire fragment
|
|
|
- if (_insertionParent && isComment(parentAnchor, ']')) {
|
|
|
- ;(parentAnchor as any as ChildItem).$idx = _insertionIndex || 0
|
|
|
- _insertionParent.$llc = parentAnchor
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- } finally {
|
|
|
- exitHydrationBoundary && exitHydrationBoundary()
|
|
|
- }
|
|
|
+ hydrateList(source, newLength)
|
|
|
} else {
|
|
|
for (let i = 0; i < newLength; i++) {
|
|
|
mount(source, i)
|
|
|
@@ -502,6 +413,98 @@ export const createFor = (
|
|
|
return block
|
|
|
}
|
|
|
|
|
|
+ function hydrateList(source: ResolvedSource, newLength: number): void {
|
|
|
+ const hydrationStart = currentHydrationNode!
|
|
|
+ let exitHydrationBoundary: (() => void) | undefined
|
|
|
+ let nextNode
|
|
|
+ const emptyLocalRange =
|
|
|
+ isComment(hydrationStart, ']') &&
|
|
|
+ isComment(hydrationStart.previousSibling!, '[')
|
|
|
+ const slotEndAnchor = getCurrentSlotEndAnchor()
|
|
|
+ const slotFallbackRange = isHydratingSlotFallbackActive() && slotEndAnchor
|
|
|
+
|
|
|
+ try {
|
|
|
+ if (emptyLocalRange && newLength) {
|
|
|
+ parentAnchor = markHydrationAnchor(hydrationStart)
|
|
|
+ exitHydrationBoundary = enterHydrationBoundary(parentAnchor)
|
|
|
+ for (let i = 0; i < newLength; i++) {
|
|
|
+ mount(source, i)
|
|
|
+ }
|
|
|
+ setCurrentHydrationNode(parentAnchor)
|
|
|
+ } else {
|
|
|
+ for (let i = 0; i < newLength; i++) {
|
|
|
+ if (isComment(currentHydrationNode!, ']')) {
|
|
|
+ nextNode = markHydrationAnchor(currentHydrationNode!)
|
|
|
+ setCurrentHydrationNode(nextNode)
|
|
|
+ } else {
|
|
|
+ nextNode = locateNextNode(currentHydrationNode!)
|
|
|
+ }
|
|
|
+ mount(source, i)
|
|
|
+ if (nextNode) setCurrentHydrationNode(nextNode)
|
|
|
+ }
|
|
|
+
|
|
|
+ // special handling transition-group + v-for, without <!--]--> marker
|
|
|
+ const resolvedAnchor =
|
|
|
+ resolveForHydrationAnchor &&
|
|
|
+ resolveForHydrationAnchor(
|
|
|
+ hydrationStart,
|
|
|
+ newLength ? nextNode : currentHydrationNode,
|
|
|
+ )
|
|
|
+ if (resolvedAnchor) {
|
|
|
+ parentAnchor = resolvedAnchor
|
|
|
+ pendingHydrationAnchor = true
|
|
|
+ } else if (slotFallbackRange && !isValidBlock(newBlocks)) {
|
|
|
+ // Slot fallback can fall through an empty/invalid `v-for`. In that
|
|
|
+ // case SSR only rendered the parent slot range, so this `v-for` has no
|
|
|
+ // own `<!--]-->` to reuse. If `hydrationStart` is not the parent slot
|
|
|
+ // end anchor, use `hydrationStart.nextSibling` as the insertion point
|
|
|
+ // so the runtime `<!--for-->` lands immediately after that local SSR
|
|
|
+ // range. Otherwise insert it before the parent slot end anchor.
|
|
|
+ const anchor =
|
|
|
+ // The invalid list still consumed local SSR item ranges.
|
|
|
+ currentHydrationNode !== hydrationStart
|
|
|
+ ? currentHydrationNode!
|
|
|
+ : // Empty source with trailing slot siblings.
|
|
|
+ hydrationStart !== slotEndAnchor
|
|
|
+ ? hydrationStart.nextSibling!
|
|
|
+ : slotEndAnchor!
|
|
|
+ parentAnchor = markHydrationAnchor(
|
|
|
+ __DEV__ ? createComment('for') : createTextNode(),
|
|
|
+ )
|
|
|
+ pendingHydrationAnchor = true
|
|
|
+ if (
|
|
|
+ currentHydrationNode === hydrationStart ||
|
|
|
+ currentHydrationNode === slotEndAnchor
|
|
|
+ ) {
|
|
|
+ setCurrentHydrationNode(hydrationStart)
|
|
|
+ }
|
|
|
+ queuePostFlushCb(() => {
|
|
|
+ const parentNode = anchor.parentNode
|
|
|
+ if (parentNode) parentNode.insertBefore(parentAnchor, anchor)
|
|
|
+ })
|
|
|
+ } else {
|
|
|
+ const close = locateHydrationBoundaryClose(currentHydrationNode!)
|
|
|
+ parentAnchor = markHydrationAnchor(close)
|
|
|
+ exitHydrationBoundary = enterHydrationBoundary(parentAnchor)
|
|
|
+ if (__DEV__ && !isComment(parentAnchor, ']')) {
|
|
|
+ throw new Error(
|
|
|
+ `v-for fragment anchor node was not found. this is likely a Vue internal bug.`,
|
|
|
+ )
|
|
|
+ }
|
|
|
+
|
|
|
+ // optimization: cache the fragment end anchor as $llc (last logical child)
|
|
|
+ // so that locateChildByLogicalIndex can skip the entire fragment
|
|
|
+ if (_insertionParent && isComment(parentAnchor, ']')) {
|
|
|
+ ;(parentAnchor as any as ChildItem).$idx = _insertionIndex || 0
|
|
|
+ _insertionParent.$llc = parentAnchor
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } finally {
|
|
|
+ exitHydrationBoundary && exitHydrationBoundary()
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
const update = (
|
|
|
{ itemRef, keyRef, indexRef }: ForBlock,
|
|
|
newItem: any,
|