Lesson 12-Vue3 Advanced Features

Vue 3, with its refactored Composition API and reactive system, provides developers with more flexible code organization and powerful extensibility. This section dives deep into Vue 3’s core advanced features, offering a comprehensive exploration from reactive programming to plugin development.

Composition API

Core Concepts of the Composition API

The setup function serves as the entry point for the Composition API, playing a critical role during component creation:

export default {
  setup(props, context) {
    // props: Destructuring loses reactivity
    const { title } = toRefs(props) 
    
    // context includes attrs, slots, emit
    const { emit } = context
    
    // Returned object is exposed to the template
    return {
      title,
      handleClick: () => emit('click')
    }
  }
}

Reactivity Basics:

  • ref: Wraps primitive values, accessed via .value.
  • reactive: Creates reactive objects, supporting nested properties.
  • toRefs: Converts a reactive object into a collection of ref objects.

Reactive Data Management Strategies

Choosing Between ref and reactive:

ScenarioRecommended APIReason
Primitive valuesrefreactive cannot directly proxy primitives
Objects/ArraysreactiveMaintains object reference, avoids losing reactivity during destructuring
Destructuring reactive objectstoRefs + reactiveKeeps destructured properties reactive
Template referencesrefSpecifically for DOM elements or component instances

Complex Scenario Example:

setup() {
  // Primitive type
  const count = ref(0)
  
  // Object type
  const user = reactive({
    name: 'Alice',
    profile: {
      age: 25
    }
  })
  
  // Destructuring with reactivity
  const { name, profile } = toRefs(user)
  
  return { count, name, profile }
}

Computed Properties and Watchers

Computed Implementation:

import { ref, computed } from 'vue'

setup() {
  const price = ref(100)
  const quantity = ref(2)
  
  // Cached computed property
  const total = computed(() => price.value * quantity.value)
  
  // Writable computed property
  const doubleCount = computed({
    get: () => count.value * 2,
    set: (val) => { count.value = val / 2 }
  })
  
  return { total, doubleCount }
}

Advanced watchEffect Usage:

import { ref, watchEffect } from 'vue'

setup() {
  const searchQuery = ref('')
  const results = ref([])
  
  // Automatically tracks dependencies
  watchEffect(async (onCleanup) => {
    const timer = setTimeout(async () => {
      results.value = await fetchResults(searchQuery.value)
    }, 500)
    
    // Cleanup side effects
    onCleanup(() => clearTimeout(timer))
  })
  
  return { searchQuery, results }
}

Lifecycle Hooks Mapping

Composition API Lifecycle Comparison:

Options APIComposition API
beforeCreatesetup()
createdsetup()
beforeMountonBeforeMount
mountedonMounted
beforeUpdateonBeforeUpdate
updatedonUpdated
beforeUnmountonBeforeUnmount
unmountedonUnmounted
errorCapturedonErrorCaptured
renderTrackedonRenderTracked
renderTriggeredonRenderTriggered

Multiple Hooks Registration Example:

import { onMounted, onUpdated, onUnmounted } from 'vue'

setup() {
  const timer = ref(null)
  
  onMounted(() => {
    timer.value = setInterval(() => {
      console.log('Component mounted')
    }, 1000)
  })
  
  onUpdated(() => {
    console.log('Component updated')
  })
  
  onUnmounted(() => {
    clearInterval(timer.value)
  })
}

Custom Hook Development Pattern

Custom Hook Example: useFetch:

// hooks/useFetch.js
import { ref } from 'vue'

export function useFetch(url) {
  const data = ref(null)
  const error = ref(null)
  const loading = ref(false)
  
  const fetchData = async () => {
    loading.value = true
    try {
      const response = await fetch(url)
      data.value = await response.json()
    } catch (err) {
      error.value = err
    } finally {
      loading.value = false
    }
  }
  
  return { data, error, loading, fetchData }
}

// Usage in component
import { useFetch } from './hooks/useFetch'

setup() {
  const { data, loading, fetchData } = useFetch('/api/user')
  
  onMounted(fetchData)
  
  return { data, loading }
}

Hook Composition Example:

// Combining multiple hooks
setup() {
  const user = useUser()
  const cart = useCart()
  const notifications = useNotifications()
  
  return { ...user, ...cart, ...notifications }
}

Render Functions and JSX

Render Function Basics

Core Parameters of the h Function:

import { h } from 'vue'

export default {
  render() {
    return h('div', 
      { class: 'container' },
      [
        h('h1', 'Title'),
        h(MyComponent, { prop: 'value' })
      ]
    )
  }
}

Dynamic Component Rendering:

const components = {
  'text': TextComponent,
  'image': ImageComponent
}

render() {
  return h(components[this.type], {
    ...this.$attrs
  })
}

JSX Configuration and Transformation

vite.config.js Configuration:

import vue from '@vitejs/plugin-vue'

export default {
  plugins: [
    vue({
      jsx: true  // Enable JSX support
    })
  ]
}

JSX Syntax Example:

export default {
  props: ['message'],
  setup(props) {
    const handleClick = () => {
      console.log(props.message)
    }
    
    return () => (
      <button onClick={handleClick}>
        {props.message}
      </button>
    )
  }
}

Dynamic Component Implementation

Advanced Dynamic Component Pattern:

const DynamicRenderer = {
  props: ['componentType', 'propsData'],
  setup(props) {
    const componentMap = {
      alert: AlertComponent,
      modal: ModalComponent
    }
    
    return () => {
      const Component = componentMap[props.componentType]
      return Component ? h(Component, props.propsData) : null
    }
  }
}

Higher-Order Component Implementation

HOC Factory Function:

function withLoading(WrappedComponent) {
  return {
    props: WrappedComponent.props,
    setup(props, { slots }) {
      const loading = ref(false)
      
      const wrappedSlots = {
        default: (props) => h(WrappedComponent, { ...props, loading })
      }
      
      return () => loading.value 
        ? h('div', 'Loading...')
        : h(WrappedComponent, { ...props, loading })
    }
  }
}

// Usage
const EnhancedComponent = withLoading(BaseComponent)

Render Performance Optimization

Key Optimization Strategies:

  1. Static Hoisting: Extract static nodes outside the render function.
  2. Patch Flags: Vue 3 automatically marks dynamic nodes.
  3. Event Handler Caching: Avoids recreating functions repeatedly.
  4. v-memo Directive: Caches subtree rendering.

Optimization Example:

// Optimize list rendering with v-memo
render() {
  return h('ul', 
    this.items.map(item => 
      h('li', 
        { key: item.id, vMemo: [item.id] },
        item.text
      )
    )
  )
}

Custom Directives and Plugin Development

Advanced Directive Implementation

Complete Directive Lifecycle:

const vFocus = {
  // When element is mounted
  mounted(el, binding) {
    if (binding.modifiers.autofocus) {
      el.focus()
    }
  },
  
  // When component updates
  updated(el, binding) {
    if (binding.value !== binding.oldValue) {
      el.style.color = binding.value ? 'red' : 'black'
    }
  },
  
  // When element is unmounted
  unmounted(el) {
    el.removeEventListener('click', handleClick)
  }
}

// Directive with parameters
const vResize = {
  mounted(el, binding) {
    const callback = binding.value
    const observer = new ResizeObserver(entries => {
      callback(entries[0].contentRect)
    })
    observer.observe(el)
    el._resizeObserver = observer
  },
  unmounted(el) {
    el._resizeObserver.disconnect()
  }
}

Directive Parameter Parsing

Modifier Handling Example:

const vDebounce = {
  mounted(el, binding) {
    const { value: fn, modifiers } = binding
    let delay = 300
    
    if (modifiers.fast) delay = 100
    if (modifiers.slow) delay = 1000
    
    let timeout
    el.addEventListener('input', () => {
      clearTimeout(timeout)
      timeout = setTimeout(() => fn(), delay)
    })
  }
}

Plugin Development Standards

Standard Plugin Structure:

// plugins/myPlugin.js
export default {
  install(app, options) {
    // 1. Register global component
    app.component('MyComponent', MyComponent)
    
    // 2. Register global directive
    app.directive('focus', vFocus)
    
    // 3. Add global method
    app.config.globalProperties.$myMethod = () => {...}
    
    // 4. Provide dependency injection
    app.provide('myInjection', options.value)
    
    // 5. Mixin global logic
    app.mixin({
      created() {
        console.log('Global mixin')
      }
    })
  }
}

// Use plugin
import { createApp } from 'vue'
import App from './App.vue'
import myPlugin from './plugins/myPlugin'

const app = createApp(App)
app.use(myPlugin, { value: 123 })
app.mount('#app')

Plugin Performance Optimization

On-Demand Plugin Loading:

// Dynamic plugin loading
export async function loadPlugin(app, pluginName) {
  const plugin = await import(`./plugins/${pluginName}`)
  app.use(plugin.default)
}

// Conditional directive registration
const conditionalDirective = {
  install(app) {
    if (process.env.NODE_ENV === 'development') {
      app.directive('debug', vDebug)
    }
  }
}

Compatibility Handling

Browser Feature Detection:

const vIntersection = {
  mounted(el, binding) {
    if ('IntersectionObserver' in window) {
      const observer = new IntersectionObserver(entries => {
        if (entries[0].isIntersecting) {
          binding.value()
        }
      })
      observer.observe(el)
      el._intersectionObserver = observer
    } else {
      // Fallback solution
      binding.value()
    }
  },
  unmounted(el) {
    if (el._intersectionObserver) {
      el._intersectionObserver.disconnect()
    }
  }
}

Polyfill Strategy:

// vite.config.js
import legacy from '@vitejs/plugin-legacy'

export default {
  plugins: [
    legacy({
      targets: ['defaults', 'not IE 11']
    })
  ]
}

By mastering Vue 3’s advanced features, developers can build more flexible, efficient, and maintainable large-scale applications. It’s recommended to gradually introduce these advanced features based on project needs and continuously optimize application performance using performance analysis tools.

Share your love