阿里云主机折上折
  • 微信号
Current Site:Index > Custom responsive effect

Custom responsive effect

Author:Chuan Chen 阅读数:5305人阅读 分类: Vue.js

Basic Concepts of Reactive Effects

One of the core components of Vue.js's reactivity system is the effect, which is responsible for tracking dependencies and re-executing when data changes. An effect is essentially a side-effect function that automatically re-runs when the reactive data it internally accesses changes.

import { reactive, effect } from 'vue'

const state = reactive({
  count: 0
})

effect(() => {
  console.log('count changed:', state.count)
})

state.count++ // Output: count changed: 1

Creating Custom Effects

Vue provides the effect API to allow us to create custom reactive side effects. The basic usage is to pass in a function directly:

const stop = effect(() => {
  // Side-effect logic
  document.title = `Count: ${state.count}`
})

// Stop the effect
stop()

The effect function returns a stop function, which can be called to cancel the effect's reactive tracking.

Effect Scheduler

We can control the execution timing of an effect using the scheduler option:

effect(
  () => {
    console.log('count changed:', state.count)
  },
  {
    scheduler(effect) {
      // Execute during the next microtask
      queueMicrotask(effect)
    }
  }
)

This is useful for implementing batch updates or asynchronous rendering.

Lazy Execution of Effects

Using the lazy option, we can create an effect without immediately executing it:

const lazyEffect = effect(
  () => {
    console.log('Lazy effect ran')
  },
  { lazy: true }
)

// Manually execute
lazyEffect()

Nested Effects

Effects can be nested, and Vue will automatically establish the correct dependency relationships:

effect(() => {
  console.log('Outer effect')
  
  effect(() => {
    console.log('Inner effect', state.count)
  })
})

Custom Dependency Tracking

We can manually control dependency tracking using track and trigger:

import { track, trigger } from 'vue'

const obj = { value: 1 }

effect(() => {
  track(obj, 'value') // Manual tracking
  console.log(obj.value)
})

// Manually trigger
trigger(obj, 'value')

Effect Scope

Vue 3.2+ introduced the effectScope API for managing a group of effects:

import { effectScope } from 'vue'

const scope = effectScope()

scope.run(() => {
  effect(() => {
    console.log(state.count)
  })
})

// Stop all effects within the scope
scope.stop()

Custom Reactive Effect Practice

Combining the above features, we can implement a custom reactive logger:

function createLoggerEffect(source, prefix = '') {
  const stop = effect(
    () => {
      console.log(prefix, JSON.stringify(source))
    },
    {
      scheduler(effect) {
        requestAnimationFrame(effect)
      }
    }
  )
  
  return {
    stop
  }
}

const logger = createLoggerEffect(state, '[State]')
// Stop logging
logger.stop()

Performance Optimization Considerations

For frequently changing reactive data, we can optimize effect performance in the following ways:

effect(() => {
  // Use computed to reduce unnecessary recalculations
  const double = computed(() => state.count * 2)
  console.log(double.value)
}, {
  // Set flush timing
  flush: 'post'
})

Comparison with watch

Both effect and watch are reactive APIs but serve different use cases:

// effect is suitable for side effects
effect(() => {
  document.title = state.title
})

// watch is suitable for observing specific value changes
watch(
  () => state.count,
  (newVal, oldVal) => {
    console.log('count changed', oldVal, '->', newVal)
  }
)

Error Handling

Error handling can be added to effects:

effect(
  () => {
    throw new Error('Effect error')
  },
  {
    onError(err) {
      console.error('Effect error:', err)
    }
  }
)

Advanced Patterns for Reactive Effects

Implementing an effect that automatically cleans up resources:

function useAutoCleanupEffect(fn) {
  let cleanup
  
  effect((onCleanup) => {
    if (cleanup) cleanup()
    
    cleanup = onCleanup(() => {
      console.log('Cleaning up...')
    })
    
    return fn()
  })
}

useAutoCleanupEffect(() => {
  const timer = setInterval(() => {
    console.log('Tick')
  }, 1000)
  
  return () => clearInterval(timer)
})

Reactive Effects and Component Lifecycle

When using effects in components, lifecycle management is important:

import { onUnmounted } from 'vue'

export default {
  setup() {
    const stop = effect(() => {
      // Component logic
    })
    
    onUnmounted(() => {
      stop()
    })
  }
}

Custom Reactive Store

A simple reactive store can be implemented based on effects:

function createStore(initialState) {
  const state = reactive(initialState)
  const subscribers = new Set()
  
  function subscribe(fn) {
    const effectFn = effect(fn, { lazy: true })
    subscribers.add(effectFn)
    effectFn()
    
    return () => {
      subscribers.delete(effectFn)
    }
  }
  
  function notify() {
    subscribers.forEach(effect => effect())
  }
  
  return {
    state,
    subscribe,
    notify
  }
}

Debugging Reactive Effects

Vue provides debugging tools for effects:

effect(
  () => {
    debugger // Can set breakpoints here
    console.log(state.count)
  },
  {
    onTrack(e) {
      console.log('tracking', e)
    },
    onTrigger(e) {
      console.log('triggering', e)
    }
  }
)

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

Front End Chuan

Front End Chuan, Chen Chuan's Code Teahouse 🍵, specializing in exorcising all kinds of stubborn bugs 💻. Daily serving baldness-warning-level development insights 🛠️, with a bonus of one-liners that'll make you laugh for ten years 🐟. Occasionally drops pixel-perfect romance brewed in a coffee cup ☕.