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.