阿里云主机折上折
  • 微信号
您当前的位置:网站首页 > 自定义响应式effect

自定义响应式effect

作者:陈川 阅读数:5303人阅读 分类: Vue.js

响应式effect的基本概念

Vue.js的响应式系统核心之一是effect,它负责追踪依赖并在数据变化时重新执行。effect本质上是一个副作用函数,当它内部访问的响应式数据发生变化时,该函数会自动重新运行。

import { reactive, effect } from 'vue'

const state = reactive({
  count: 0
})

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

state.count++ // 输出: count changed: 1

创建自定义effect

Vue提供了effectAPI让我们可以创建自定义的响应式副作用。基础用法是直接传入一个函数:

const stop = effect(() => {
  // 副作用逻辑
  document.title = `Count: ${state.count}`
})

// 停止effect
stop()

effect函数返回一个停止函数,调用它可以取消该effect的响应式追踪。

effect的调度器

我们可以通过scheduler选项控制effect的执行时机:

effect(
  () => {
    console.log('count changed:', state.count)
  },
  {
    scheduler(effect) {
      // 在下一次微任务时执行
      queueMicrotask(effect)
    }
  }
)

这在实现批量更新或异步渲染时非常有用。

effect的懒执行

通过lazy选项可以创建但不立即执行的effect:

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

// 手动执行
lazyEffect()

嵌套effect

effect可以嵌套使用,Vue会自动建立正确的依赖关系:

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

自定义依赖收集

我们可以通过tracktrigger手动控制依赖收集:

import { track, trigger } from 'vue'

const obj = { value: 1 }

effect(() => {
  track(obj, 'value') // 手动追踪
  console.log(obj.value)
})

// 手动触发
trigger(obj, 'value')

effect作用域

Vue 3.2+引入了effectScopeAPI,用于管理一组effect:

import { effectScope } from 'vue'

const scope = effectScope()

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

// 停止所有scope内的effect
scope.stop()

自定义响应式effect实践

结合以上特性,我们可以实现一个自定义的响应式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]')
// 停止日志记录
logger.stop()

性能优化考虑

对于高频变化的响应式数据,可以通过以下方式优化effect性能:

effect(() => {
  // 使用computed减少不必要的重新计算
  const double = computed(() => state.count * 2)
  console.log(double.value)
}, {
  // 设置flush时机
  flush: 'post'
})

与watch的对比

effectwatch都是响应式API,但有不同的使用场景:

// effect适合副作用操作
effect(() => {
  document.title = state.title
})

// watch适合观察特定值的变化
watch(
  () => state.count,
  (newVal, oldVal) => {
    console.log('count changed', oldVal, '->', newVal)
  }
)

错误处理

可以为effect添加错误处理:

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

响应式effect的高级模式

实现一个自动清理资源的effect:

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)
})

响应式effect与组件生命周期

在组件中使用effect时,需要注意生命周期管理:

import { onUnmounted } from 'vue'

export default {
  setup() {
    const stop = effect(() => {
      // 组件逻辑
    })
    
    onUnmounted(() => {
      stop()
    })
  }
}

自定义响应式存储

基于effect可以实现简单的响应式存储:

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
  }
}

响应式effect的调试

Vue提供了effect调试工具:

effect(
  () => {
    debugger // 可以在这里打断点
    console.log(state.count)
  },
  {
    onTrack(e) {
      console.log('tracking', e)
    },
    onTrigger(e) {
      console.log('triggering', e)
    }
  }
)

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

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

前端川

前端川,陈川的代码茶馆🍵,专治各种不服的Bug退散符💻,日常贩卖秃头警告级的开发心得🛠️,附赠一行代码笑十年的摸鱼宝典🐟,偶尔掉落咖啡杯里泡开的像素级浪漫☕。‌