timesafari
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

542 lines
13 KiB

<template>
<div class="heavy-component">
<h2>Heavy Data Processing Component</h2>
<!-- Data processing controls -->
<div class="controls">
<button @click="processData" :disabled="isProcessing">
{{ isProcessing ? 'Processing...' : 'Process Data' }}
</button>
<button @click="clearResults" :disabled="isProcessing">
Clear Results
</button>
</div>
<!-- Processing status -->
<div v-if="isProcessing" class="processing-status">
<div class="progress-bar">
<div class="progress-fill" :style="{ width: progress + '%' }"></div>
</div>
<p>Processing {{ processedCount }} of {{ totalItems }} items...</p>
</div>
<!-- Results display -->
<div v-if="processedData.length > 0" class="results">
<h3>Processed Results ({{ processedData.length }} items)</h3>
<!-- Filter controls -->
<div class="filters">
<input
v-model="searchTerm"
placeholder="Search items..."
class="search-input"
/>
<select v-model="sortBy" class="sort-select">
<option value="name">Sort by Name</option>
<option value="id">Sort by ID</option>
<option value="processed">Sort by Processed Date</option>
</select>
</div>
<!-- Results list -->
<div class="results-list">
<div
v-for="item in filteredAndSortedData"
:key="item.id"
class="result-item"
>
<div class="item-header">
<span class="item-name">{{ item.name }}</span>
<span class="item-id">#{{ item.id }}</span>
</div>
<div class="item-details">
<span class="processed-date">
Processed: {{ formatDate(item.processedAt) }}
</span>
<span class="processing-time">
Time: {{ item.processingTime }}ms
</span>
</div>
</div>
</div>
<!-- Pagination -->
<div v-if="totalPages > 1" class="pagination">
<button
@click="previousPage"
:disabled="currentPage === 1"
class="page-btn"
>
Previous
</button>
<span class="page-info">
Page {{ currentPage }} of {{ totalPages }}
</span>
<button
@click="nextPage"
:disabled="currentPage === totalPages"
class="page-btn"
>
Next
</button>
</div>
</div>
<!-- Performance metrics -->
<div v-if="performanceMetrics" class="performance-metrics">
<h4>Performance Metrics</h4>
<div class="metrics-grid">
<div class="metric">
<span class="metric-label">Total Processing Time:</span>
<span class="metric-value">{{ performanceMetrics.totalTime }}ms</span>
</div>
<div class="metric">
<span class="metric-label">Average per Item:</span>
<span class="metric-value">{{ performanceMetrics.averageTime }}ms</span>
</div>
<div class="metric">
<span class="metric-label">Memory Usage:</span>
<span class="metric-value">{{ performanceMetrics.memoryUsage }}MB</span>
</div>
</div>
</div>
</div>
</template>
<script lang="ts">
import { Component, Vue, Prop, Emit } from 'vue-facing-decorator';
interface ProcessedItem {
id: number;
name: string;
processedAt: Date;
processingTime: number;
result: any;
}
interface PerformanceMetrics {
totalTime: number;
averageTime: number;
memoryUsage: number;
}
/**
* Heavy Component for Data Processing
*
* Demonstrates a component that performs intensive data processing
* and would benefit from lazy loading to avoid blocking the main thread.
*
* @author Matthew Raymer
* @version 1.0.0
*/
@Component({
name: 'HeavyComponent'
})
export default class HeavyComponent extends Vue {
@Prop({ required: true }) readonly data!: {
items: Array<{ id: number; name: string }>;
filters: Record<string, any>;
sortBy: string;
};
// Component state
isProcessing = false;
processedData: ProcessedItem[] = [];
progress = 0;
processedCount = 0;
totalItems = 0;
// UI state
searchTerm = '';
sortBy = 'name';
currentPage = 1;
itemsPerPage = 50;
// Performance tracking
performanceMetrics: PerformanceMetrics | null = null;
startTime = 0;
// Computed properties
get filteredAndSortedData(): ProcessedItem[] {
let filtered = this.processedData;
// Apply search filter
if (this.searchTerm) {
filtered = filtered.filter(item =>
item.name.toLowerCase().includes(this.searchTerm.toLowerCase())
);
}
// Apply sorting
filtered.sort((a, b) => {
switch (this.sortBy) {
case 'name':
return a.name.localeCompare(b.name);
case 'id':
return a.id - b.id;
case 'processed':
return b.processedAt.getTime() - a.processedAt.getTime();
default:
return 0;
}
});
return filtered;
}
get paginatedData(): ProcessedItem[] {
const start = (this.currentPage - 1) * this.itemsPerPage;
const end = start + this.itemsPerPage;
return this.filteredAndSortedData.slice(start, end);
}
get totalPages(): number {
return Math.ceil(this.filteredAndSortedData.length / this.itemsPerPage);
}
// Lifecycle hooks
mounted(): void {
console.log('[HeavyComponent] Component mounted with', this.data.items.length, 'items');
this.totalItems = this.data.items.length;
}
// Methods
async processData(): Promise<void> {
if (this.isProcessing) return;
this.isProcessing = true;
this.progress = 0;
this.processedCount = 0;
this.processedData = [];
this.startTime = performance.now();
console.log('[HeavyComponent] Starting data processing...');
try {
// Process items in batches to avoid blocking the UI
const batchSize = 10;
const items = this.data.items;
for (let i = 0; i < items.length; i += batchSize) {
const batch = items.slice(i, i + batchSize);
// Process batch
await this.processBatch(batch);
// Update progress
this.processedCount = Math.min(i + batchSize, items.length);
this.progress = (this.processedCount / items.length) * 100;
// Allow UI to update
await this.$nextTick();
// Small delay to prevent overwhelming the UI
await new Promise(resolve => setTimeout(resolve, 10));
}
// Calculate performance metrics
this.calculatePerformanceMetrics();
// Emit completion event
this.$emit('data-processed', {
totalItems: this.processedData.length,
processingTime: performance.now() - this.startTime,
metrics: this.performanceMetrics
});
console.log('[HeavyComponent] Data processing completed');
} catch (error) {
console.error('[HeavyComponent] Processing error:', error);
this.$emit('processing-error', error);
} finally {
this.isProcessing = false;
}
}
private async processBatch(batch: Array<{ id: number; name: string }>): Promise<void> {
const processedBatch = await Promise.all(
batch.map(async (item) => {
const itemStartTime = performance.now();
// Simulate heavy processing
await this.simulateHeavyProcessing(item);
const processingTime = performance.now() - itemStartTime;
return {
id: item.id,
name: item.name,
processedAt: new Date(),
processingTime: Math.round(processingTime),
result: this.generateResult(item)
};
})
);
this.processedData.push(...processedBatch);
}
private async simulateHeavyProcessing(item: { id: number; name: string }): Promise<void> {
// Simulate CPU-intensive work
const complexity = item.name.length * item.id;
const iterations = Math.min(complexity, 1000); // Cap at 1000 iterations
for (let i = 0; i < iterations; i++) {
// Simulate work
Math.sqrt(i) * Math.random();
}
// Simulate async work
await new Promise(resolve => setTimeout(resolve, Math.random() * 10));
}
private generateResult(item: { id: number; name: string }): any {
return {
hash: this.generateHash(item.name + item.id),
category: this.categorizeItem(item),
score: Math.random() * 100,
tags: this.generateTags(item)
};
}
private generateHash(input: string): string {
let hash = 0;
for (let i = 0; i < input.length; i++) {
const char = input.charCodeAt(i);
hash = ((hash << 5) - hash) + char;
hash = hash & hash; // Convert to 32-bit integer
}
return hash.toString(16);
}
private categorizeItem(item: { id: number; name: string }): string {
const categories = ['A', 'B', 'C', 'D', 'E'];
return categories[item.id % categories.length];
}
private generateTags(item: { id: number; name: string }): string[] {
const tags = ['important', 'urgent', 'review', 'archive', 'featured'];
return tags.filter((_, index) => (item.id + index) % 3 === 0);
}
private calculatePerformanceMetrics(): void {
const totalTime = performance.now() - this.startTime;
const averageTime = totalTime / this.processedData.length;
// Simulate memory usage calculation
const memoryUsage = this.processedData.length * 0.1; // 0.1MB per item
this.performanceMetrics = {
totalTime: Math.round(totalTime),
averageTime: Math.round(averageTime),
memoryUsage: Math.round(memoryUsage * 100) / 100
};
}
clearResults(): void {
this.processedData = [];
this.performanceMetrics = null;
this.searchTerm = '';
this.currentPage = 1;
console.log('[HeavyComponent] Results cleared');
}
previousPage(): void {
if (this.currentPage > 1) {
this.currentPage--;
}
}
nextPage(): void {
if (this.currentPage < this.totalPages) {
this.currentPage++;
}
}
formatDate(date: Date): string {
return date.toLocaleString();
}
// Event emitters
@Emit('data-processed')
emitDataProcessed(data: any): any {
return data;
}
@Emit('processing-error')
emitProcessingError(error: Error): Error {
return error;
}
}
</script>
<style scoped>
.heavy-component {
padding: 20px;
border: 1px solid #ddd;
border-radius: 8px;
background: #f9f9f9;
}
.controls {
display: flex;
gap: 10px;
margin-bottom: 20px;
}
.controls button {
padding: 8px 16px;
border: 1px solid #ccc;
border-radius: 4px;
background: #fff;
cursor: pointer;
transition: background-color 0.2s;
}
.controls button:hover:not(:disabled) {
background: #e9ecef;
}
.controls button:disabled {
opacity: 0.6;
cursor: not-allowed;
}
.processing-status {
margin-bottom: 20px;
}
.progress-bar {
width: 100%;
height: 20px;
background: #e9ecef;
border-radius: 10px;
overflow: hidden;
margin-bottom: 10px;
}
.progress-fill {
height: 100%;
background: linear-gradient(90deg, #007bff, #0056b3);
transition: width 0.3s ease;
}
.results {
margin-top: 20px;
}
.filters {
display: flex;
gap: 10px;
margin-bottom: 20px;
}
.search-input,
.sort-select {
padding: 8px;
border: 1px solid #ccc;
border-radius: 4px;
}
.search-input {
flex: 1;
}
.results-list {
max-height: 400px;
overflow-y: auto;
border: 1px solid #ddd;
border-radius: 4px;
background: #fff;
}
.result-item {
padding: 12px;
border-bottom: 1px solid #eee;
}
.result-item:last-child {
border-bottom: none;
}
.item-header {
display: flex;
justify-content: space-between;
margin-bottom: 8px;
}
.item-name {
font-weight: bold;
}
.item-id {
color: #666;
font-size: 0.9em;
}
.item-details {
display: flex;
gap: 20px;
font-size: 0.85em;
color: #666;
}
.pagination {
display: flex;
justify-content: center;
align-items: center;
gap: 15px;
margin-top: 20px;
}
.page-btn {
padding: 6px 12px;
border: 1px solid #ccc;
border-radius: 4px;
background: #fff;
cursor: pointer;
}
.page-btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.page-info {
font-size: 0.9em;
color: #666;
}
.performance-metrics {
margin-top: 20px;
padding: 15px;
background: #e8f4fd;
border-radius: 4px;
}
.metrics-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 15px;
margin-top: 10px;
}
.metric {
display: flex;
justify-content: space-between;
padding: 8px;
background: #fff;
border-radius: 4px;
}
.metric-label {
font-weight: bold;
color: #333;
}
.metric-value {
color: #007bff;
font-weight: bold;
}
</style>