Skip to content

Advanced Features

This section introduces advanced features of EwVueComponent, such as async components, error boundaries, plugin extensions, and performance optimization.

Async Component Loading

EwVueComponent supports async components for code splitting and on-demand loading:

1. Basic Async Components

vue
<template>
  <div class="demo">
    <div class="controls">
      <button @click="loadComponent('UserProfile')">User Profile</button>
      <button @click="loadComponent('Settings')">Settings</button>
      <button @click="loadComponent('Dashboard')">Dashboard</button>
    </div>
    
    <div v-if="loading" class="loading">
      <div class="spinner"></div>
      <p>Loading...</p>
    </div>
    
    <EwVueComponent 
      v-else-if="currentComponent"
      :is="currentComponent" 
      @error="handleError"
    />
  </div>
</template>

<script setup>
import { ref } from 'vue'
import { EwVueComponent } from 'ew-vue-component'

const currentComponent = ref(null)
const loading = ref(false)

const componentMap = {
  UserProfile: () => import('./components/UserProfile.vue'),
  Settings: () => import('./components/Settings.vue'),
  Dashboard: () => import('./components/Dashboard.vue')
}

const loadComponent = async (name) => {
  loading.value = true
  try {
    // Simulate network delay
    await new Promise(resolve => setTimeout(resolve, 1000))
    currentComponent.value = componentMap[name]
  } catch (error) {
    handleError(error)
  } finally {
    loading.value = false
  }
}

const handleError = (error) => {
  console.error('Async component loading failed:', error)
  loading.value = false
}
</script>

<style scoped>
.loading {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 2rem;
}

.spinner {
  width: 40px;
  height: 40px;
  border: 4px solid #f3f3f3;
  border-top: 4px solid #667eea;
  border-radius: 50%;
  animation: spin 1s linear infinite;
  margin-bottom: 1rem;
}

@keyframes spin {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}
</style>

2. Async Components with Loading Progress

vue
<template>
  <div class="demo">
    <button @click="loadHeavyComponent">Load Heavy Component</button>
    
    <div v-if="loading" class="loading-state">
      <div class="progress-bar">
        <div class="progress" :style="{ width: progress + '%' }"></div>
      </div>
      <p>Loading progress: {{ progress }}%</p>
    </div>
    
    <EwVueComponent 
      v-else-if="currentComponent"
      :is="currentComponent" 
      @error="handleError"
    />
  </div>
</template>

<script setup>
import { ref } from 'vue'
import { EwVueComponent } from 'ew-vue-component'

const currentComponent = ref(null)
const loading = ref(false)
const progress = ref(0)

const loadHeavyComponent = async () => {
  loading.value = true
  progress.value = 0
  
  // Simulate loading progress
  const progressInterval = setInterval(() => {
    progress.value += 10
    if (progress.value >= 100) {
      clearInterval(progressInterval)
    }
  }, 100)
  
  try {
    await new Promise(resolve => setTimeout(resolve, 2000))
    currentComponent.value = () => import('./components/HeavyComponent.vue')
  } catch (error) {
    handleError(error)
  } finally {
    loading.value = false
    progress.value = 100
  }
}

const handleError = (error) => {
  console.error('Component loading failed:', error)
  loading.value = false
}
</script>

<style scoped>
.loading-state {
  padding: 2rem;
  text-align: center;
}

.progress-bar {
  width: 100%;
  height: 8px;
  background: #e2e8f0;
  border-radius: 4px;
  overflow: hidden;
  margin-bottom: 1rem;
}

.progress {
  height: 100%;
  background: linear-gradient(90deg, #667eea, #764ba2);
  transition: width 0.3s ease;
}
</style>

Error Boundaries and Fallback Handling

EwVueComponent provides comprehensive error handling mechanisms:

1. Basic Error Handling

vue
<template>
  <div class="demo">
    <button @click="loadErrorComponent">Load Error Component</button>
    
    <EwVueComponent 
      :is="currentComponent"
      @error="handleError"
    />
  </div>
</template>

<script setup>
import { ref } from 'vue'
import { EwVueComponent } from 'ew-vue-component'

const currentComponent = ref('div')

const loadErrorComponent = () => {
  // Intentionally load a non-existent component
  currentComponent.value = 'NonExistentComponent'
}

const handleError = (error) => {
  console.error('Component error:', error)
  // Fallback to default component
  currentComponent.value = 'div'
}
</script>

2. Using Fallback Components

vue
<template>
  <div class="demo">
    <button @click="loadUnstableComponent">Load Unstable Component</button>
    
    <EwVueComponent 
      :is="currentComponent"
      fallback="div"
      @error="handleError"
    >
      <template #default>
        <p>Main content</p>
      </template>
    </EwVueComponent>
  </div>
</template>

<script setup>
import { ref } from 'vue'
import { EwVueComponent } from 'ew-vue-component'

const currentComponent = ref('div')

const loadUnstableComponent = () => {
  // Simulate unstable component
  currentComponent.value = Math.random() > 0.5 ? 'UnstableComponent' : 'div'
}

const handleError = (error) => {
  console.error('Component error, using fallback:', error)
}
</script>

3. Custom Error Components

vue
<template>
  <div class="demo">
    <button @click="loadBrokenComponent">Load Broken Component</button>
    
    <EwVueComponent 
      :is="currentComponent"
      :error-component="ErrorComponent"
      @error="handleError"
    />
  </div>
</template>

<script setup>
import { ref, h } from 'vue'
import { EwVueComponent } from 'ew-vue-component'

const currentComponent = ref('div')

const ErrorComponent = {
  props: ['error', 'retry'],
  render() {
    return h('div', { class: 'error-component' }, [
      h('h3', 'Component Loading Failed'),
      h('p', `Error: ${this.error?.message || 'Unknown error'}`),
      h('button', {
        onClick: () => this.retry?.()
      }, 'Retry')
    ])
  }
}

const loadBrokenComponent = () => {
  currentComponent.value = 'BrokenComponent'
}

const handleError = (error) => {
  console.error('Component error, showing custom error component:', error)
}
</script>

<style scoped>
.error-component {
  padding: 2rem;
  text-align: center;
  background: #fef2f2;
  border: 1px solid #fecaca;
  border-radius: 0.5rem;
  color: #dc2626;
}

.error-component button {
  margin-top: 1rem;
  padding: 0.5rem 1rem;
  background: #dc2626;
  color: white;
  border: none;
  border-radius: 0.25rem;
  cursor: pointer;
}
</style>

Caching and Performance Optimization

1. Component Caching

vue
<template>
  <div class="demo">
    <div class="controls">
      <button @click="loadComponent('Heavy')">Load Heavy Component</button>
      <button @click="loadComponent('Complex')">Load Complex Component</button>
      <button @click="clearCache">Clear Cache</button>
    </div>
    
    <p>Cache status: {{ cacheStatus }}</p>
    
    <EwVueComponent 
      :is="currentComponent"
      :cache="true"
      :cache-key="cacheKey"
      :cache-ttl="300000"
      @error="handleError"
    />
  </div>
</template>

<script setup>
import { ref, computed } from 'vue'
import { EwVueComponent } from 'ew-vue-component'

const currentComponent = ref(null)
const componentType = ref('Heavy')

const componentMap = {
  Heavy: () => import('./components/HeavyComponent.vue'),
  Complex: () => import('./components/ComplexComponent.vue')
}

const cacheKey = computed(() => `component-${componentType.value}`)
const cacheStatus = ref('No cache')

const loadComponent = (type) => {
  componentType.value = type
  currentComponent.value = componentMap[type]
  cacheStatus.value = `Loaded ${type} component`
}

const clearCache = () => {
  // This would trigger cache clearing in the component
  cacheStatus.value = 'Cache cleared'
}

const handleError = (error) => {
  console.error('Component error:', error)
}
</script>

2. Performance Monitoring

vue
<template>
  <div class="demo">
    <button @click="loadComponentWithMonitoring">Load with Monitoring</button>
    
    <div v-if="performanceData" class="performance-info">
      <h4>Performance Data:</h4>
      <p>Render time: {{ performanceData.renderTime }}ms</p>
      <p>Load time: {{ performanceData.loadTime }}ms</p>
    </div>
    
    <EwVueComponent 
      :is="currentComponent"
      :plugins="[performancePlugin]"
      @error="handleError"
    />
  </div>
</template>

<script setup>
import { ref } from 'vue'
import { EwVueComponent } from 'ew-vue-component'

const currentComponent = ref(null)
const performanceData = ref(null)

const performancePlugin = {
  name: 'performance',
  beforeRender(component, props, context) {
    context.data.startTime = performance.now()
  },
  afterRender(component, props, context) {
    const endTime = performance.now()
    const renderTime = endTime - context.data.startTime
    
    performanceData.value = {
      renderTime: renderTime.toFixed(2),
      loadTime: (endTime - context.data.loadStartTime || 0).toFixed(2)
    }
  }
}

const loadComponentWithMonitoring = async () => {
  const loadStartTime = performance.now()
  
  try {
    currentComponent.value = () => import('./components/TestComponent.vue')
  } catch (error) {
    handleError(error)
  }
}

const handleError = (error) => {
  console.error('Component error:', error)
}
</script>

<style scoped>
.performance-info {
  margin: 1rem 0;
  padding: 1rem;
  background: #f0f9ff;
  border: 1px solid #0ea5e9;
  border-radius: 0.5rem;
}
</style>

Plugin System

1. Creating Custom Plugins

javascript
// Custom logging plugin
const customLogPlugin = {
  name: 'custom-log',
  
  beforeRender(component, props, context) {
    console.log(`🚀 Rendering component: ${component.name || component}`)
    context.data.renderStartTime = performance.now()
  },
  
  afterRender(component, props, context) {
    const duration = performance.now() - context.data.renderStartTime
    console.log(`✅ Component rendered in ${duration.toFixed(2)}ms`)
  },
  
  onError(error, context) {
    console.error(`❌ Component error: ${error.message}`)
    // Send error to monitoring service
    this.reportError(error, context)
  },
  
  reportError(error, context) {
    // Implement error reporting logic
    fetch('/api/errors', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        error: error.message,
        stack: error.stack,
        component: context.component,
        timestamp: new Date().toISOString()
      })
    })
  }
}

2. Using Multiple Plugins

vue
<template>
  <EwVueComponent 
    :is="currentComponent"
    :plugins="plugins"
    @error="handleError"
  />
</template>

<script setup>
import { ref } from 'vue'
import { EwVueComponent } from 'ew-vue-component'

const currentComponent = ref('div')

const plugins = [
  {
    name: 'logger',
    beforeRender(component, props, context) {
      context.utils.log('Starting render:', component)
    }
  },
  {
    name: 'analytics',
    afterRender(component, props, context) {
      // Track component usage
      analytics.track('component_rendered', {
        component: component.name || component,
        timestamp: Date.now()
      })
    }
  },
  {
    name: 'error-reporter',
    onError(error, context) {
      // Report errors to external service
      errorService.report(error, context)
    }
  }
]

const handleError = (error) => {
  console.error('Component error:', error)
}
</script>

Best Practices

1. Component Organization

javascript
// utils/componentLoader.js
export const createComponentLoader = (components) => {
  const cache = new Map()
  
  return {
    load: async (name) => {
      if (cache.has(name)) {
        return cache.get(name)
      }
      
      const component = await components[name]()
      cache.set(name, component)
      return component
    },
    
    preload: async (names) => {
      const promises = names.map(name => this.load(name))
      await Promise.all(promises)
    },
    
    clear: () => {
      cache.clear()
    }
  }
}

// Usage
const loader = createComponentLoader({
  UserProfile: () => import('@/components/UserProfile.vue'),
  Settings: () => import('@/components/Settings.vue'),
  Dashboard: () => import('@/components/Dashboard.vue')
})

2. Error Boundary Pattern

vue
<template>
  <EwVueComponent 
    :is="currentComponent"
    :fallback="FallbackComponent"
    :error-component="ErrorComponent"
    @error="handleGlobalError"
  >
    <template #default>
      <AppLoader v-if="loading" />
    </template>
  </EwVueComponent>
</template>

<script setup>
import { ref, h } from 'vue'
import { EwVueComponent } from 'ew-vue-component'
import AppLoader from '@/components/AppLoader.vue'

const currentComponent = ref(null)
const loading = ref(false)

const FallbackComponent = {
  render() {
    return h('div', { class: 'fallback' }, [
      h('h3', 'Something went wrong'),
      h('p', 'Please try refreshing the page')
    ])
  }
}

const ErrorComponent = {
  props: ['error', 'retry'],
  render() {
    return h('div', { class: 'error-boundary' }, [
      h('h3', 'Component Error'),
      h('p', this.error?.message),
      h('button', { onClick: this.retry }, 'Retry')
    ])
  }
}

const handleGlobalError = (error) => {
  // Global error handling
  console.error('Global component error:', error)
  
  // Report to error monitoring service
  if (typeof window !== 'undefined' && window.Sentry) {
    window.Sentry.captureException(error)
  }
}
</script>

3. Performance Optimization

vue
<template>
  <EwVueComponent 
    :is="currentComponent"
    :cache="shouldCache"
    :cache-key="cacheKey"
    :cache-ttl="cacheTtl"
    :plugins="performancePlugins"
  />
</template>

<script setup>
import { ref, computed } from 'vue'
import { EwVueComponent } from 'ew-vue-component'

const currentComponent = ref(null)
const componentSize = ref('medium') // small, medium, large

// Dynamic caching based on component size
const shouldCache = computed(() => componentSize.value !== 'small')
const cacheTtl = computed(() => {
  switch (componentSize.value) {
    case 'large': return 600000 // 10 minutes
    case 'medium': return 300000 // 5 minutes
    default: return 60000 // 1 minute
  }
})

const cacheKey = computed(() => 
  `${currentComponent.value?.name || 'unknown'}-${componentSize.value}`
)

const performancePlugins = [
  {
    name: 'performance-monitor',
    beforeRender(component, props, context) {
      if (componentSize.value === 'large') {
        context.utils.log('Loading large component, monitoring performance...')
      }
    },
    afterRender(component, props, context) {
      const duration = context.data.renderTime
      if (duration > 100) {
        context.utils.warn(`Slow render detected: ${duration}ms`)
      }
    }
  }
]
</script>

Released under the MIT License.