0
# Lazy Components
1
2
Vue components for lazy loading content and images with full Vue 3 composition API support.
3
4
## Capabilities
5
6
### lazy-component
7
8
A wrapper component that lazily renders its content when it becomes visible in the viewport.
9
10
```typescript { .api }
11
/**
12
* Lazy component that renders content only when visible
13
*/
14
interface LazyComponentProps {
15
/** HTML tag to render as wrapper (default: 'div') */
16
tag?: string;
17
}
18
19
/**
20
* Events emitted by lazy-component
21
*/
22
interface LazyComponentEvents {
23
/** Emitted when component becomes visible and content is shown */
24
show: (visible: boolean) => void;
25
}
26
27
/**
28
* Slots available in lazy-component
29
*/
30
interface LazyComponentSlots {
31
/** Default slot content that will be lazily rendered */
32
default: () => VNode[];
33
}
34
```
35
36
**Usage Examples:**
37
38
```vue
39
<template>
40
<!-- Basic lazy component -->
41
<lazy-component @show="onComponentShow">
42
<img src="/heavy-image.jpg" alt="Heavy image">
43
<p>This content loads when visible</p>
44
</lazy-component>
45
46
<!-- Custom wrapper tag -->
47
<lazy-component tag="section" @show="onSectionShow">
48
<h2>Lazy Section</h2>
49
<video src="/large-video.mp4" controls></video>
50
</lazy-component>
51
52
<!-- In a list -->
53
<div class="image-gallery">
54
<lazy-component
55
v-for="(item, index) in galleryItems"
56
:key="item.id"
57
@show="() => onItemShow(index)"
58
>
59
<img :src="item.imageUrl" :alt="item.title">
60
<h3>{{ item.title }}</h3>
61
</lazy-component>
62
</div>
63
</template>
64
65
<script setup>
66
import { ref } from "vue";
67
68
const galleryItems = ref([
69
{ id: 1, imageUrl: "/gallery1.jpg", title: "Image 1" },
70
{ id: 2, imageUrl: "/gallery2.jpg", title: "Image 2" },
71
{ id: 3, imageUrl: "/gallery3.jpg", title: "Image 3" },
72
]);
73
74
const onComponentShow = (visible) => {
75
console.log("Component is now visible:", visible);
76
};
77
78
const onSectionShow = (visible) => {
79
console.log("Section became visible:", visible);
80
};
81
82
const onItemShow = (index) => {
83
console.log(`Gallery item ${index} is now visible`);
84
};
85
</script>
86
```
87
88
### lazy-image
89
90
A specialized component for lazy loading images with built-in state management and error handling.
91
92
```typescript { .api }
93
/**
94
* Lazy image component with built-in loading states
95
*/
96
interface LazyImageProps {
97
/** Image source URL or configuration object */
98
src: string | VueLazyloadImageOptions;
99
/** HTML tag to render (default: 'img') */
100
tag?: string;
101
}
102
103
/**
104
* Image options for lazy-image component
105
*/
106
interface VueLazyloadImageOptions {
107
/** Image source URL */
108
src: string;
109
/** Error fallback image URL */
110
error?: string;
111
/** Loading placeholder image URL */
112
loading?: string;
113
/** Maximum loading attempts */
114
attempt?: number;
115
}
116
117
/**
118
* Slots available in lazy-image component
119
*/
120
interface LazyImageSlots {
121
/** Optional slot content rendered alongside image */
122
default?: () => VNode[];
123
}
124
```
125
126
**Usage Examples:**
127
128
```vue
129
<template>
130
<!-- Basic lazy image -->
131
<lazy-image src="/photo.jpg" />
132
133
<!-- Image with configuration -->
134
<lazy-image :src="{
135
src: '/high-res-photo.jpg',
136
loading: '/loading-placeholder.png',
137
error: '/error-fallback.png',
138
attempt: 3
139
}" />
140
141
<!-- Custom wrapper tag -->
142
<lazy-image
143
tag="figure"
144
:src="imageConfig"
145
>
146
<figcaption>Image caption content</figcaption>
147
</lazy-image>
148
149
<!-- Dynamic image source -->
150
<lazy-image
151
:src="computedImageSrc"
152
@load="onImageLoaded"
153
@error="onImageError"
154
/>
155
</template>
156
157
<script setup>
158
import { computed, ref } from "vue";
159
160
const imageConfig = ref({
161
src: "/portrait.jpg",
162
loading: "/skeleton-loader.svg",
163
error: "/broken-image.svg",
164
attempt: 5,
165
});
166
167
const imageQuality = ref("medium");
168
const imageName = ref("landscape");
169
170
const computedImageSrc = computed(() => ({
171
src: `/images/${imageName.value}-${imageQuality.value}.jpg`,
172
loading: "/placeholders/image-loading.svg",
173
error: "/placeholders/image-error.svg",
174
}));
175
176
const onImageLoaded = () => {
177
console.log("Lazy image successfully loaded");
178
};
179
180
const onImageError = () => {
181
console.error("Failed to load lazy image");
182
};
183
</script>
184
```
185
186
### Component Integration Patterns
187
188
Advanced usage patterns for integrating lazy components with other Vue features.
189
190
**With Vue Router:**
191
192
```vue
193
<template>
194
<router-view v-slot="{ Component }">
195
<lazy-component @show="onRouteComponentShow">
196
<component :is="Component" />
197
</lazy-component>
198
</router-view>
199
</template>
200
201
<script setup>
202
import { useRoute } from "vue-router";
203
204
const route = useRoute();
205
206
const onRouteComponentShow = () => {
207
console.log("Route component visible:", route.path);
208
};
209
</script>
210
```
211
212
**With Suspense:**
213
214
```vue
215
<template>
216
<Suspense>
217
<template #default>
218
<lazy-component @show="onAsyncComponentShow">
219
<AsyncComponent />
220
</lazy-component>
221
</template>
222
<template #fallback>
223
<div class="loading-placeholder">Loading async component...</div>
224
</template>
225
</Suspense>
226
</template>
227
228
<script setup>
229
import { defineAsyncComponent } from "vue";
230
231
const AsyncComponent = defineAsyncComponent(() => import("./HeavyComponent.vue"));
232
233
const onAsyncComponentShow = () => {
234
console.log("Async component is now visible");
235
};
236
</script>
237
```
238
239
**With Teleport:**
240
241
```vue
242
<template>
243
<lazy-component @show="onModalContentShow">
244
<Teleport to="#modal-container">
245
<div class="modal-content">
246
<lazy-image :src="modalImageSrc" />
247
</div>
248
</Teleport>
249
</lazy-component>
250
</template>
251
252
<script setup>
253
const modalImageSrc = ref("/modal-background.jpg");
254
255
const onModalContentShow = () => {
256
console.log("Modal content is visible");
257
};
258
</script>
259
```
260
261
### Performance Optimization
262
263
Optimizing lazy component performance for large lists and complex layouts.
264
265
**Virtual Scrolling Integration:**
266
267
```vue
268
<template>
269
<div class="virtual-list" ref="scrollContainer">
270
<lazy-component
271
v-for="item in visibleItems"
272
:key="item.id"
273
@show="() => trackItemView(item.id)"
274
>
275
<ItemComponent :item="item" />
276
</lazy-component>
277
</div>
278
</template>
279
280
<script setup>
281
import { ref, computed } from "vue";
282
283
const scrollContainer = ref();
284
const allItems = ref([/* large array of items */]);
285
286
// Only render items in viewport + buffer
287
const visibleItems = computed(() => {
288
// Implementation for virtual scrolling logic
289
return allItems.value.slice(startIndex.value, endIndex.value);
290
});
291
292
const trackItemView = (itemId) => {
293
// Analytics tracking when item becomes visible
294
console.log("Item viewed:", itemId);
295
};
296
</script>
297
```
298
299
**Intersection Observer Configuration:**
300
301
```vue
302
<template>
303
<lazy-component
304
@show="onHighPriorityShow"
305
data-lazy-priority="high"
306
>
307
<CriticalContent />
308
</lazy-component>
309
310
<lazy-component
311
@show="onLowPriorityShow"
312
data-lazy-priority="low"
313
>
314
<OptionalContent />
315
</lazy-component>
316
</template>
317
318
<script setup>
319
// Components automatically inherit observer settings from plugin config
320
// Can be customized via global configuration during plugin installation
321
322
const onHighPriorityShow = () => {
323
// Handle high priority content visibility
324
};
325
326
const onLowPriorityShow = () => {
327
// Handle low priority content visibility
328
};
329
</script>
330
```
331
332
### Component State Management
333
334
Managing component states and lifecycle hooks.
335
336
```vue
337
<template>
338
<lazy-component @show="handleComponentShow">
339
<div class="content-wrapper">
340
<lazy-image
341
:src="imageSource"
342
@loading="onImageLoading"
343
@loaded="onImageLoaded"
344
@error="onImageError"
345
/>
346
<div class="content-text">
347
{{ contentText }}
348
</div>
349
</div>
350
</lazy-component>
351
</template>
352
353
<script setup>
354
import { ref, nextTick } from "vue";
355
356
const imageSource = ref("/initial-image.jpg");
357
const contentText = ref("Initial content");
358
const isComponentVisible = ref(false);
359
360
const handleComponentShow = async (visible) => {
361
isComponentVisible.value = visible;
362
363
if (visible) {
364
// Load additional resources when component becomes visible
365
await loadAdditionalResources();
366
}
367
};
368
369
const onImageLoading = () => {
370
console.log("Image started loading");
371
};
372
373
const onImageLoaded = () => {
374
console.log("Image finished loading");
375
// Trigger any post-load animations or updates
376
nextTick(() => {
377
// DOM updates after image load
378
});
379
};
380
381
const onImageError = () => {
382
console.error("Image failed to load");
383
// Fallback behavior
384
imageSource.value = "/fallback-image.jpg";
385
};
386
387
const loadAdditionalResources = async () => {
388
// Fetch additional data when component is visible
389
const response = await fetch("/api/additional-content");
390
const data = await response.json();
391
contentText.value = data.text;
392
};
393
</script>
394
```