forked from jsnbuchanan/crowd-funder-for-time-pwa
docs: update build pattern conversion plan with consistent naming and mode handling
- Change build:* naming from hyphen to colon (build:web-dev → build:web:dev) - Add missing build:web:test and build:web:prod scripts - Update build:electron:dev to include electron startup (build + start) - Remove hardcoded --mode electron to allow proper mode override - Add comprehensive mode override behavior documentation - Fix mode conflicts between hardcoded and passed --mode arguments The plan now properly supports: - Development builds with default --mode development - Testing builds with explicit --mode test override - Production builds with explicit --mode production override - Consistent naming across all platforms (web, capacitor, electron)
This commit is contained in:
293
src/components/LazyLoadingExample.vue
Normal file
293
src/components/LazyLoadingExample.vue
Normal file
@@ -0,0 +1,293 @@
|
||||
<template>
|
||||
<div class="lazy-loading-example">
|
||||
<!-- Loading state with Suspense -->
|
||||
<Suspense>
|
||||
<template #default>
|
||||
<!-- Main content with lazy-loaded components -->
|
||||
<div class="content">
|
||||
<h1>Lazy Loading Example</h1>
|
||||
|
||||
<!-- Lazy-loaded heavy component -->
|
||||
<LazyHeavyComponent
|
||||
v-if="showHeavyComponent"
|
||||
:data="heavyComponentData"
|
||||
@data-processed="handleDataProcessed"
|
||||
/>
|
||||
|
||||
<!-- Conditionally loaded components -->
|
||||
<LazyQRScanner
|
||||
v-if="showQRScanner"
|
||||
@qr-detected="handleQRDetected"
|
||||
/>
|
||||
|
||||
<LazyThreeJSViewer
|
||||
v-if="showThreeJS"
|
||||
:model-url="threeJSModelUrl"
|
||||
@model-loaded="handleModelLoaded"
|
||||
/>
|
||||
|
||||
<!-- Route-based lazy loading -->
|
||||
<router-view v-slot="{ Component }">
|
||||
<component :is="Component" />
|
||||
</router-view>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- Loading fallback -->
|
||||
<template #fallback>
|
||||
<div class="loading-fallback">
|
||||
<div class="spinner"></div>
|
||||
<p>Loading components...</p>
|
||||
</div>
|
||||
</template>
|
||||
</Suspense>
|
||||
|
||||
<!-- Control buttons -->
|
||||
<div class="controls">
|
||||
<button @click="toggleHeavyComponent">
|
||||
{{ showHeavyComponent ? 'Hide' : 'Show' }} Heavy Component
|
||||
</button>
|
||||
<button @click="toggleQRScanner">
|
||||
{{ showQRScanner ? 'Hide' : 'Show' }} QR Scanner
|
||||
</button>
|
||||
<button @click="toggleThreeJS">
|
||||
{{ showThreeJS ? 'Hide' : 'Show' }} 3D Viewer
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Vue, Prop, Watch } from 'vue-facing-decorator';
|
||||
import { defineAsyncComponent } from 'vue';
|
||||
|
||||
/**
|
||||
* Lazy Loading Example Component
|
||||
*
|
||||
* Demonstrates various lazy loading patterns with vue-facing-decorator:
|
||||
* - defineAsyncComponent for heavy components
|
||||
* - Conditional loading based on user interaction
|
||||
* - Suspense for loading states
|
||||
* - Route-based lazy loading
|
||||
*
|
||||
* @author Matthew Raymer
|
||||
* @version 1.0.0
|
||||
*/
|
||||
@Component({
|
||||
name: 'LazyLoadingExample',
|
||||
components: {
|
||||
// Lazy-loaded components with loading and error states
|
||||
LazyHeavyComponent: defineAsyncComponent({
|
||||
loader: () => import('./sub-components/HeavyComponent.vue'),
|
||||
loadingComponent: {
|
||||
template: '<div class="loading">Loading heavy component...</div>'
|
||||
},
|
||||
errorComponent: {
|
||||
template: '<div class="error">Failed to load heavy component</div>'
|
||||
},
|
||||
delay: 200, // Show loading component after 200ms
|
||||
timeout: 10000 // Timeout after 10 seconds
|
||||
}),
|
||||
|
||||
LazyQRScanner: defineAsyncComponent({
|
||||
loader: () => import('./sub-components/QRScannerComponent.vue'),
|
||||
loadingComponent: {
|
||||
template: '<div class="loading">Initializing QR scanner...</div>'
|
||||
},
|
||||
errorComponent: {
|
||||
template: '<div class="error">QR scanner not available</div>'
|
||||
}
|
||||
}),
|
||||
|
||||
LazyThreeJSViewer: defineAsyncComponent({
|
||||
loader: () => import('./sub-components/ThreeJSViewer.vue'),
|
||||
loadingComponent: {
|
||||
template: '<div class="loading">Loading 3D viewer...</div>'
|
||||
},
|
||||
errorComponent: {
|
||||
template: '<div class="error">3D viewer failed to load</div>'
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
export default class LazyLoadingExample extends Vue {
|
||||
// Component state
|
||||
@Prop({ default: false }) readonly initialLoadHeavy!: boolean;
|
||||
|
||||
// Reactive properties
|
||||
showHeavyComponent = false;
|
||||
showQRScanner = false;
|
||||
showThreeJS = false;
|
||||
|
||||
// Component data
|
||||
heavyComponentData = {
|
||||
items: Array.from({ length: 1000 }, (_, i) => ({ id: i, name: `Item ${i}` })),
|
||||
filters: { category: 'all', status: 'active' },
|
||||
sortBy: 'name'
|
||||
};
|
||||
|
||||
threeJSModelUrl = '/models/lupine_plant/scene.gltf';
|
||||
|
||||
// Computed properties
|
||||
get isLoadingAnyComponent(): boolean {
|
||||
return this.showHeavyComponent || this.showQRScanner || this.showThreeJS;
|
||||
}
|
||||
|
||||
get componentCount(): number {
|
||||
let count = 0;
|
||||
if (this.showHeavyComponent) count++;
|
||||
if (this.showQRScanner) count++;
|
||||
if (this.showThreeJS) count++;
|
||||
return count;
|
||||
}
|
||||
|
||||
// Lifecycle hooks
|
||||
mounted(): void {
|
||||
console.log('[LazyLoadingExample] Component mounted');
|
||||
|
||||
// Initialize based on props
|
||||
if (this.initialLoadHeavy) {
|
||||
this.showHeavyComponent = true;
|
||||
}
|
||||
|
||||
// Preload critical components
|
||||
this.preloadCriticalComponents();
|
||||
}
|
||||
|
||||
// Methods
|
||||
toggleHeavyComponent(): void {
|
||||
this.showHeavyComponent = !this.showHeavyComponent;
|
||||
console.log('[LazyLoadingExample] Heavy component toggled:', this.showHeavyComponent);
|
||||
}
|
||||
|
||||
toggleQRScanner(): void {
|
||||
this.showQRScanner = !this.showQRScanner;
|
||||
console.log('[LazyLoadingExample] QR scanner toggled:', this.showQRScanner);
|
||||
}
|
||||
|
||||
toggleThreeJS(): void {
|
||||
this.showThreeJS = !this.showThreeJS;
|
||||
console.log('[LazyLoadingExample] ThreeJS viewer toggled:', this.showThreeJS);
|
||||
}
|
||||
|
||||
handleDataProcessed(data: any): void {
|
||||
console.log('[LazyLoadingExample] Data processed:', data);
|
||||
// Handle processed data from heavy component
|
||||
}
|
||||
|
||||
handleQRDetected(qrData: string): void {
|
||||
console.log('[LazyLoadingExample] QR code detected:', qrData);
|
||||
// Handle QR code data
|
||||
}
|
||||
|
||||
handleModelLoaded(modelInfo: any): void {
|
||||
console.log('[LazyLoadingExample] 3D model loaded:', modelInfo);
|
||||
// Handle 3D model loaded event
|
||||
}
|
||||
|
||||
/**
|
||||
* Preload critical components for better UX
|
||||
*/
|
||||
private preloadCriticalComponents(): void {
|
||||
// Preload components that are likely to be used
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
// In production, preload based on user behavior patterns
|
||||
this.preloadComponent(() => import('./sub-components/HeavyComponent.vue'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Preload a component without rendering it
|
||||
*/
|
||||
private preloadComponent(componentLoader: () => Promise<any>): void {
|
||||
componentLoader().catch(error => {
|
||||
console.warn('[LazyLoadingExample] Preload failed:', error);
|
||||
});
|
||||
}
|
||||
|
||||
// Watchers
|
||||
@Watch('showHeavyComponent')
|
||||
onHeavyComponentToggle(newValue: boolean): void {
|
||||
if (newValue) {
|
||||
// Component is being shown - could trigger analytics
|
||||
console.log('[LazyLoadingExample] Heavy component shown');
|
||||
}
|
||||
}
|
||||
|
||||
@Watch('componentCount')
|
||||
onComponentCountChange(newCount: number): void {
|
||||
console.log('[LazyLoadingExample] Active component count:', newCount);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.lazy-loading-example {
|
||||
padding: 20px;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.content {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.controls {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
flex-wrap: wrap;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.controls button {
|
||||
padding: 10px 20px;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
background: #f8f9fa;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
|
||||
.controls button:hover {
|
||||
background: #e9ecef;
|
||||
}
|
||||
|
||||
.loading-fallback {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 40px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.spinner {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border: 4px solid #f3f3f3;
|
||||
border-top: 4px solid #3498db;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.loading {
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.error {
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
color: #dc3545;
|
||||
background: #f8d7da;
|
||||
border: 1px solid #f5c6cb;
|
||||
border-radius: 4px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user