前语
effectScope
是个强壮的api
,官网原话:创立一个 effect 效果域,可以捕获其中所创立的响应式副效果 (即计算属性和侦听器),这样捕获到的副效果可以一起处理
,在RFC对其有更具体的解说,在vueuse库中更有对其的妙用
为什么要EffectScope
RFC中现已对其做了解说,就是为了方便处理副效果,手动收集、整理副效果如下:
收集
const disposables = []
const counter = ref(0)
const doubled = computed(() => counter.value * 2)
disposables.push(() => stop(doubled.effect))
const stopWatch1 = watchEffect(() => {
console.log(`counter: ${counter.value}`)
})
disposables.push(stopWatch1)
const stopWatch2 = watch(doubled, () => {
console.log(doubled.value)
})
disposables.push(stopWatch2)
铲除
disposables.forEach((f) => f())
disposables = []
上述过程需人工进行,关于杂乱的逻辑收集成本较高,忘掉收集也或许造成内存泄漏等问题。所以设计EffectScope
处理该问题,关于开发库的同学更友好
EffectScope运用
function effectScope(detached?: boolean): EffectScope
interface EffectScope {
run<T>(fn: () => T): T | undefined // 假如效果域不活跃就为 undefined
stop(): void
}
子父EffectScope
detached
表示是否阻断和父级的联络,若为true
则表示与父级断开相关,履行父级stop
办法时会递归中止子集的监听,但子集detached
为true
时则不会中止,如下:
let nestedScope
let childScope
const parentScope = effectScope()
parentScope.run(() => {
const doubled = computed(() => counter.value * 2)
// 与父级断开链接
nestedScope = effectScope(true /* detached */)
nestedScope.run(() => {
watch(doubled, () => console.log('nestedScope', doubled.value))
})
// 父级断开监听也会断开
childScope = effectScope()
childScope.run(() => {
watch(doubled, () => console.log('childScope', doubled.value))
})
watchEffect(() => console.log('Count: ', doubled.value))
})
// 中止parentScope、childScope监听
parentScope.stop()
// 中止nestedScope监听
nestedScope.stop()
onScopeDispose
中止监听时触发改函数onScopeDispose
,效果类似onUnmounted
import { onScopeDispose } from 'vue'
const scope = effectScope()
scope.run(() => {
onScopeDispose(() => {
console.log('cleaned!')
})
})
// 中止监听,触发onScopeDispose
scope.stop()
功能提升
关于特定场景运用EffectScope
功能更好更合理
function useMouse() {
const x = ref(0)
const y = ref(0)
function handler(e) {
x.value = e.x
y.value = e.y
}
window.addEventListener('mousemove', handler)
onUnmounted(() => {
window.removeEventListener('mousemove', handler)
})
return { x, y }
}
关于useMouse
获取坐标假如多个组件运用则会添加多个监听,添加功能成本,这个时候运用EffectScope
更友好
function useMouse() {
const x = ref(0)
const y = ref(0)
function handler(e) {
x.value = e.x
y.value = e.y
}
window.addEventListener('mousemove', handler)
onScopeDispose(() => {
window.removeEventListener('mousemove', handler)
})
return { x, y }
}
function createSharedComposable(composable) {
let subscribers = 0
let state, scope
const dispose = () => {
if (scope && --subscribers <= 0) {
scope.stop()
state = scope = null
}
}
return (...args) => {
subscribers++
if (!state) {
scope = effectScope(true)
state = scope.run(() => composable(...args))
}
onScopeDispose(dispose)
return state
}
}
const useSharedMouse = createSharedComposable(useMouse)
export default useSharedMouse
这样无论多少组件运用,只会进行一次监听,不运用时一起铲除监听
灵敏的状况办理
获取坐标demo
现已可以表现出EffectScope
状况办理的效果,咱们可以模拟store
完结一套更灵敏的状况办理又或用于子父组件、兄弟组件、大局通讯
创立函数统一办理状况
// useGlobalState
import { effectScope } from '@vue/composition-api'
export default run => {
let isChange = false
let state
const scope = effectScope(true)
return () => {
// 防止重复触发
if (!isChange) {
state = scope.run(run)
isChange = true
}
return state
}
}
创立store
// store.js
import { computed, ref } from '@vue/composition-api'
import useGlobalState from './useGlobalState'
export default useGlobalState(
() => {
// state
const count = ref(0)
// getters
const doubleCount = computed(() => count.value * 2)
// actions
function increment() {
count.value++
}
return { count, doubleCount, increment }
}
)
在不同组件运用
// A.vue
import useStore from '@/hooks/useStore'
const { count, doubleCount, increment } = useStore()
// B.vue
import useStore from '@/hooks/useStore'
const { count, doubleCount, increment } = useStore()
在A/B组件获取的count
、doubleCount
数据共同,所以effectScope
一起供给新的的状况办理方式,某种场景可以代替vuex
若是为了完结数据耐久化,参加缓存即可。vueuse
现已供给这种状况办理方式createGlobalState,若可以满足需求,直接运用即可
结语
时刻准备着