963 markdown files reformatted with the repository's pinned prettier so pnpm format:check covers the folded tree like every other repo file. The formatter's embedded-language pass also normalized code fences (TS semicolons, closed HTML tags in examples, lowercased CSS hex colors, one renumbered list that skipped an index). Alphanumeric token deltas vs the fold commit were audited file-by-file; all are formatter-equivalent markup normalizations plus the four sanitized skills.
4.0 KiB
4.0 KiB
Async Component Lazy Hydration Strategies (Vue 3.5+)
Rule
In Vue 3.5+, use hydration strategies with async components to control when SSR-rendered components become interactive. Import hydration strategies individually for tree-shaking.
Why This Matters
In SSR applications, hydrating all components immediately can block the main thread and delay interactivity. Lazy hydration allows non-critical components to become interactive only when needed, improving Time to Interactive (TTI) metrics.
Available Hydration Strategies
hydrateOnIdle
Hydrates when the browser is idle using requestIdleCallback:
<script setup>
import { defineAsyncComponent, hydrateOnIdle } from 'vue';
// Good for non-critical, below-the-fold content
const AsyncFooter = defineAsyncComponent({
loader: () => import('./Footer.vue'),
hydrate: hydrateOnIdle(),
});
// With max timeout (in case idle never occurs)
const AsyncSidebar = defineAsyncComponent({
loader: () => import('./Sidebar.vue'),
hydrate: hydrateOnIdle(5000), // Max 5 seconds
});
</script>
hydrateOnVisible
Hydrates when element enters the viewport via IntersectionObserver:
<script setup>
import { defineAsyncComponent, hydrateOnVisible } from 'vue';
// Good for content below the fold
const AsyncComments = defineAsyncComponent({
loader: () => import('./Comments.vue'),
hydrate: hydrateOnVisible(),
});
// With root margin for earlier hydration
const AsyncRelatedPosts = defineAsyncComponent({
loader: () => import('./RelatedPosts.vue'),
hydrate: hydrateOnVisible({ rootMargin: '100px' }),
});
</script>
hydrateOnMediaQuery
Hydrates when a media query matches:
<script setup>
import { defineAsyncComponent, hydrateOnMediaQuery } from 'vue';
// Only hydrate on mobile devices
const AsyncMobileMenu = defineAsyncComponent({
loader: () => import('./MobileMenu.vue'),
hydrate: hydrateOnMediaQuery('(max-width: 768px)'),
});
// Only hydrate on desktop
const AsyncDesktopSidebar = defineAsyncComponent({
loader: () => import('./DesktopSidebar.vue'),
hydrate: hydrateOnMediaQuery('(min-width: 1024px)'),
});
</script>
hydrateOnInteraction
Hydrates when user interacts with the component:
<script setup>
import { defineAsyncComponent, hydrateOnInteraction } from 'vue';
// Hydrate on click
const AsyncDropdown = defineAsyncComponent({
loader: () => import('./Dropdown.vue'),
hydrate: hydrateOnInteraction('click'),
});
// Hydrate on multiple events
const AsyncTooltip = defineAsyncComponent({
loader: () => import('./Tooltip.vue'),
hydrate: hydrateOnInteraction(['mouseover', 'focus']),
});
</script>
Note: The triggering event is replayed after hydration completes, so user interaction is not lost.
Custom Hydration Strategy
import { defineAsyncComponent, type HydrationStrategy } from 'vue';
const hydrateAfterAnimation: HydrationStrategy = (hydrate, forEachElement) => {
// Wait for page load animation to complete
const timeout = setTimeout(hydrate, 1000);
return () => clearTimeout(timeout); // Cleanup
};
const AsyncWidget = defineAsyncComponent({
loader: () => import('./Widget.vue'),
hydrate: hydrateAfterAnimation,
});
Key Points
- Hydration strategies only apply to SSR - they have no effect in client-only apps
- Import strategies individually:
import { hydrateOnIdle } from 'vue' hydrateOnInteractionreplays the triggering event after hydration- Use
hydrateOnVisiblefor below-the-fold content - Use
hydrateOnIdlefor non-critical components - Use
hydrateOnMediaQueryfor device-specific components
Strategy Selection Guide
| Component Type | Recommended Strategy |
|---|---|
| Footer, related content | hydrateOnIdle |
| Below-the-fold sections | hydrateOnVisible |
| Interactive widgets | hydrateOnInteraction |
| Mobile-only components | hydrateOnMediaQuery |
| Critical above-the-fold | No strategy (immediate) |