Skip to content

Plugin API

Overview

EwVueComponent provides a powerful plugin system that allows developers to extend component functionality, add custom behaviors such as logging, performance monitoring, error handling, and more.

Plugin Structure

typescript
interface Plugin {
  name: string
  beforeRender?: (component: any, props: any, context: PluginContext) => void
  afterRender?: (component: any, props: any, context: PluginContext) => void
  onError?: (error: Error, context: PluginContext) => void
  install?: (context: PluginContext) => void
  uninstall?: (context: PluginContext) => void
}

interface PluginContext {
  component: any
  data: Record<string, any>
  utils: {
    warn: (message: string) => void
    log: (message: string, data?: any) => void
  }
}

Basic Plugins

1. Log Plugin

javascript
const logPlugin = {
  name: 'log',
  
  beforeRender(component, props, context) {
    context.utils.log(`Starting to render component: ${component.name || component}`, {
      component,
      props: Object.keys(props)
    })
  },
  
  afterRender(component, props, context) {
    context.utils.log(`Component render completed: ${component.name || component}`)
  },
  
  onError(error, context) {
    context.utils.warn(`Component render error: ${error.message}`)
  }
}

2. Performance Plugin

javascript
const performancePlugin = {
  name: 'performance',
  
  beforeRender(component, props, context) {
    performance.mark(`render-start-${component.name || 'unknown'}`)
    context.data.renderStartTime = performance.now()
  },
  
  afterRender(component, props, context) {
    const endTime = performance.now()
    const duration = endTime - context.data.renderStartTime
    
    performance.mark(`render-end-${component.name || 'unknown'}`)
    performance.measure(
      `render-${component.name || 'unknown'}`,
      `render-start-${component.name || 'unknown'}`,
      `render-end-${component.name || 'unknown'}`
    )
    
    context.utils.log(`Component render time: ${duration.toFixed(2)}ms`)
  }
}

3. Error Handling Plugin

javascript
const errorPlugin = {
  name: 'error-handler',
  
  onError(error, context) {
    // Report error to server
    this.reportError(error, context)
    
    // Show user-friendly error message
    this.showUserError(error)
  },
  
  reportError(error, context) {
    // Send error to monitoring service
    console.error('Error reported:', {
      error: error.message,
      stack: error.stack,
      component: context.component,
      timestamp: new Date().toISOString()
    })
  },
  
  showUserError(error) {
    // Show user-friendly error message
    console.warn('Component loading failed, please try again later')
  }
}

Using Plugins

1. Basic Usage

vue
<template>
  <EwVueComponent 
    :is="currentComponent"
    :plugins="[logPlugin, performancePlugin]"
  />
</template>

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

const currentComponent = ref('div')

const logPlugin = {
  name: 'log',
  beforeRender(component, props, context) {
    console.log('Before render:', component, props)
  },
  afterRender(component, props, context) {
    console.log('After render:', component, props)
  }
}

const performancePlugin = {
  name: 'performance',
  beforeRender(component, props, context) {
    performance.mark('component-render-start')
  },
  afterRender(component, props, context) {
    performance.mark('component-render-end')
    performance.measure('component-render', 'component-render-start', 'component-render-end')
  }
}
</script>

2. Dynamic Plugin Management

vue
<template>
  <div class="demo">
    <div class="controls">
      <label>
        <input v-model="enableLogging" type="checkbox" />
        Enable Logging
      </label>
      <label>
        <input v-model="enablePerformance" type="checkbox" />
        Enable Performance Monitoring
      </label>
      <label>
        <input v-model="enableErrorHandling" type="checkbox" />
        Enable Error Handling
      </label>
    </div>
    
    <EwVueComponent 
      :is="currentComponent"
      :plugins="activePlugins"
    />
  </div>
</template>

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

const currentComponent = ref('div')
const enableLogging = ref(true)
const enablePerformance = ref(false)
const enableErrorHandling = ref(true)

const logPlugin = {
  name: 'log',
  beforeRender(component, props, context) {
    console.log('Before render:', component, props)
  },
  afterRender(component, props, context) {
    console.log('After render:', component, props)
  }
}

const performancePlugin = {
  name: 'performance',
  beforeRender(component, props, context) {
    performance.mark('component-render-start')
  },
  afterRender(component, props, context) {
    performance.mark('component-render-end')
    performance.measure('component-render', 'component-render-start', 'component-render-end')
  }
}

const errorPlugin = {
  name: 'error-handler',
  onError(error, context) {
    console.error('Component error:', error)
  }
}

const activePlugins = computed(() => {
  const plugins = []
  
  if (enableLogging.value) plugins.push(logPlugin)
  if (enablePerformance.value) plugins.push(performancePlugin)
  if (enableErrorHandling.value) plugins.push(errorPlugin)
  
  return plugins
})
</script>

3. Plugin Configuration

vue
<template>
  <EwVueComponent 
    :is="currentComponent"
    :plugins="configuredPlugins"
  />
</template>

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

const currentComponent = ref('div')

// Configurable log plugin
const createLogPlugin = (options = {}) => ({
  name: 'log',
  beforeRender(component, props, context) {
    if (options.logBefore) {
      console.log('Before render:', component, props)
    }
  },
  afterRender(component, props, context) {
    if (options.logAfter) {
      console.log('After render:', component, props)
    }
  },
  onError(error, context) {
    if (options.logErrors) {
      console.error('Error:', error)
    }
  }
})

const configuredPlugins = [
  createLogPlugin({ logBefore: true, logAfter: true, logErrors: true }),
  createPerformancePlugin({ measureTime: true })
]
</script>

Advanced Plugins

1. State Management Plugin

javascript
const statePlugin = {
  name: 'state-manager',
  
  install(context) {
    // Initialize plugin state
    context.data.state = {
      renderCount: 0,
      lastRenderTime: null,
      errors: []
    }
  },
  
  beforeRender(component, props, context) {
    context.data.state.renderCount++
    context.data.state.lastRenderTime = new Date()
  },
  
  onError(error, context) {
    context.data.state.errors.push({
      message: error.message,
      timestamp: new Date(),
      component: context.component
    })
  },
  
  uninstall(context) {
    // Clean up plugin state
    console.log('Plugin state:', context.data.state)
    delete context.data.state
  }
}

2. Cache Plugin

javascript
const cachePlugin = {
  name: 'cache-manager',
  
  install(context) {
    context.data.cache = new Map()
  },
  
  beforeRender(component, props, context) {
    const cacheKey = this.generateCacheKey(component, props)
    
    if (context.data.cache.has(cacheKey)) {
      // Use cached result
      const cachedResult = context.data.cache.get(cacheKey)
      context.utils.log('Using cached result:', cachedResult)
      return cachedResult
    }
  },
  
  afterRender(component, props, context) {
    const cacheKey = this.generateCacheKey(component, props)
    const result = { component, props, timestamp: Date.now() }
    
    context.data.cache.set(cacheKey, result)
    context.utils.log('Cached result:', result)
  },
  
  generateCacheKey(component, props) {
    return `${component.name || component}-${JSON.stringify(props)}`
  },
  
  uninstall(context) {
    context.data.cache.clear()
  }
}

3. Validation Plugin

javascript
const validationPlugin = {
  name: 'validator',
  
  beforeRender(component, props, context) {
    // Validate component type
    if (!this.isValidComponent(component)) {
      throw new Error(`Invalid component type: ${component}`)
    }
    
    // Validate required props
    if (component.props) {
      this.validateProps(component.props, props)
    }
  },
  
  isValidComponent(component) {
    return typeof component === 'string' || 
           typeof component === 'function' || 
           (typeof component === 'object' && component.render)
  },
  
  validateProps(requiredProps, providedProps) {
    for (const [key, prop] of Object.entries(requiredProps)) {
      if (prop.required && !(key in providedProps)) {
        throw new Error(`Missing required prop: ${key}`)
      }
    }
  }
}

Plugin Composition

1. Plugin Factory

javascript
// plugins/factory.js
export const createPluginFactory = () => {
  const plugins = new Map()
  
  return {
    register(name, plugin) {
      plugins.set(name, plugin)
    },
    
    get(name) {
      return plugins.get(name)
    },
    
    create(config) {
      return Object.entries(config).map(([name, options]) => {
        const plugin = plugins.get(name)
        if (!plugin) {
          throw new Error(`Plugin not found: ${name}`)
        }
        return typeof plugin === 'function' ? plugin(options) : plugin
      })
    }
  }
}

// Usage example
const factory = createPluginFactory()

factory.register('log', (options) => ({
  name: 'log',
  beforeRender(component, props, context) {
    if (options.enabled) {
      console.log('Before render:', component, props)
    }
  }
}))

factory.register('performance', performancePlugin)

const plugins = factory.create({
  log: { enabled: true },
  performance: {}
})

2. Plugin Chain

javascript
const pluginChain = {
  name: 'plugin-chain',
  
  install(context) {
    context.data.pluginChain = []
  },
  
  beforeRender(component, props, context) {
    // Execute plugin chain
    for (const plugin of context.data.pluginChain) {
      if (plugin.beforeRender) {
        plugin.beforeRender(component, props, context)
      }
    }
  },
  
  afterRender(component, props, context) {
    // Execute plugin chain in reverse order
    for (let i = context.data.pluginChain.length - 1; i >= 0; i--) {
      const plugin = context.data.pluginChain[i]
      if (plugin.afterRender) {
        plugin.afterRender(component, props, context)
      }
    }
  },
  
  addPlugin(plugin, context) {
    context.data.pluginChain.push(plugin)
  },
  
  removePlugin(pluginName, context) {
    context.data.pluginChain = context.data.pluginChain.filter(
      plugin => plugin.name !== pluginName
    )
  }
}

Plugin Hooks

beforeRender

Called before component rendering starts:

javascript
const beforeRenderPlugin = {
  name: 'before-render',
  beforeRender(component, props, context) {
    // Prepare data
    context.data.startTime = Date.now()
    
    // Validate inputs
    if (!component) {
      throw new Error('Component is required')
    }
    
    // Log information
    context.utils.log('Starting render for:', component.name || component)
  }
}

afterRender

Called after component rendering completes:

javascript
const afterRenderPlugin = {
  name: 'after-render',
  afterRender(component, props, context) {
    // Calculate duration
    const duration = Date.now() - context.data.startTime
    
    // Log completion
    context.utils.log(`Render completed in ${duration}ms`)
    
    // Track analytics
    analytics.track('component_rendered', {
      component: component.name || component,
      duration,
      props: Object.keys(props)
    })
  }
}

onError

Called when an error occurs during rendering:

javascript
const errorHandlerPlugin = {
  name: 'error-handler',
  onError(error, context) {
    // Log error
    context.utils.warn('Render error:', error.message)
    
    // Report to monitoring service
    errorReporter.report(error, {
      component: context.component,
      timestamp: new Date(),
      userAgent: navigator.userAgent
    })
    
    // Show user notification
    notificationService.show({
      type: 'error',
      message: 'Something went wrong. Please try again.',
      duration: 5000
    })
  }
}

Best Practices

1. Plugin Naming

Use descriptive names and follow conventions:

javascript
// Good
const userAnalyticsPlugin = { name: 'user-analytics' }
const performanceMonitorPlugin = { name: 'performance-monitor' }

// Bad
const plugin1 = { name: 'p1' }
const myPlugin = { name: 'plugin' }

2. Error Handling in Plugins

Always handle errors gracefully:

javascript
const robustPlugin = {
  name: 'robust-plugin',
  
  beforeRender(component, props, context) {
    try {
      // Plugin logic here
      this.doSomething(component, props)
    } catch (error) {
      context.utils.warn('Plugin error:', error.message)
      // Don't let plugin errors break the component
    }
  },
  
  doSomething(component, props) {
    // Implementation
  }
}

3. Performance Considerations

Keep plugins lightweight and efficient:

javascript
const efficientPlugin = {
  name: 'efficient-plugin',
  
  beforeRender(component, props, context) {
    // Only do work when necessary
    if (this.shouldProcess(component)) {
      this.process(component, props, context)
    }
  },
  
  shouldProcess(component) {
    // Quick check to avoid unnecessary work
    return component && typeof component === 'object'
  },
  
  process(component, props, context) {
    // Expensive operation only when needed
  }
}

4. Plugin Lifecycle Management

Properly manage plugin lifecycle:

javascript
const lifecyclePlugin = {
  name: 'lifecycle-plugin',
  
  install(context) {
    // Initialize resources
    context.data.resources = this.createResources()
    console.log('Plugin installed')
  },
  
  beforeRender(component, props, context) {
    // Use resources
    context.data.resources.use()
  },
  
  uninstall(context) {
    // Clean up resources
    if (context.data.resources) {
      context.data.resources.cleanup()
      delete context.data.resources
    }
    console.log('Plugin uninstalled')
  },
  
  createResources() {
    return {
      use() { /* implementation */ },
      cleanup() { /* cleanup implementation */ }
    }
  }
}

Released under the MIT License.