Skip to content

Commit

Permalink
fix(effectScope): should have a vaild scope with component
Browse files Browse the repository at this point in the history
  • Loading branch information
antfu committed Aug 9, 2021
1 parent c34f9ea commit da21873
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 25 deletions.
39 changes: 30 additions & 9 deletions src/apis/effectScope.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
ComponentInternalInstance,
getCurrentInstance,
getVueConstructor,
withCurrentInstanceTrackingDisabled,
Expand All @@ -9,7 +10,7 @@ import { warn } from './warn'
let activeEffectScope: EffectScope | undefined
const effectScopeStack: EffectScope[] = []

export class EffectScope {
class EffectScopeImpl {
active = true
effects: EffectScope[] = []
cleanups: (() => void)[] = []
Expand All @@ -19,15 +20,8 @@ export class EffectScope {
**/
vm: Vue

constructor(detached = false) {
let vm: Vue = undefined!
withCurrentInstanceTrackingDisabled(() => {
vm = defineComponentInstance(getVueConstructor())
})
constructor(vm: Vue) {
this.vm = vm
if (!detached) {
recordEffectScope(this)
}
}

run<T>(fn: () => T): T | undefined {
Expand Down Expand Up @@ -68,6 +62,19 @@ export class EffectScope {
}
}

export class EffectScope extends EffectScopeImpl {
constructor(detached = false) {
let vm: Vue = undefined!
withCurrentInstanceTrackingDisabled(() => {
vm = defineComponentInstance(getVueConstructor())
})
super(vm)
if (!detached) {
recordEffectScope(this)
}
}
}

export function recordEffectScope(
effect: EffectScope,
scope?: EffectScope | null
Expand Down Expand Up @@ -107,3 +114,17 @@ export function onScopeDispose(fn: () => void) {
export function getCurrentScopeVM() {
return getCurrentScope()?.vm || getCurrentInstance()?.proxy
}

/**
* @internal
**/
export function bindCurrentScopeToVM(
vm: ComponentInternalInstance
): EffectScope {
if (!vm.scope) {
const scope = new EffectScopeImpl(vm.proxy) as EffectScope
vm.scope = scope
vm.proxy.$on('hook:destroyed', () => scope.stop())
}
return vm.scope
}
7 changes: 4 additions & 3 deletions src/apis/lifecycle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
setCurrentInstance,
getCurrentInstance,
ComponentInternalInstance,
setCurrentVue2Instance,
} from '../runtimeContext'
import { currentVMInFn } from '../utils/helper'

Expand Down Expand Up @@ -33,12 +34,12 @@ function injectHookOption(

function wrapHookCall(vm: ComponentInstance, fn: Function): Function {
return (...args: any) => {
let preVm = getCurrentInstance()?.proxy
setCurrentInstance(vm)
let preVm = getCurrentInstance()
setCurrentVue2Instance(vm)
try {
return fn(...args)
} finally {
setCurrentInstance(preVm!)
setCurrentInstance(preVm)
}
}
}
Expand Down
28 changes: 18 additions & 10 deletions src/runtimeContext.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { VueConstructor, VNode } from 'vue'
import { bindCurrentScopeToVM, EffectScope } from './apis/effectScope'
import { ComponentInstance, Data } from './component'
import {
assert,
Expand Down Expand Up @@ -27,7 +28,7 @@ try {
}

let vueConstructor: VueConstructor | null = null
let currentInstance: ComponentInstance | null = null
let currentInstance: ComponentInternalInstance | null = null
let currentInstanceTracking = true

const PluginInstalledFlag = '__composition_api_installed__'
Expand Down Expand Up @@ -93,10 +94,17 @@ export function withCurrentInstanceTrackingDisabled(fn: () => void) {
}
}

export function setCurrentInstance(vm: ComponentInstance | null) {
export function setCurrentVue2Instance(vm: ComponentInstance | null) {
if (!currentInstanceTracking) return
// currentInstance?.$scopedSlots
setCurrentInstance(vm ? toVue3ComponentInstance(vm) : vm)
}

export function setCurrentInstance(vm: ComponentInternalInstance | null) {
if (!currentInstanceTracking) return
const prev = currentInstance
prev?.scope.off()
currentInstance = vm
currentInstance?.scope.on()
}

export type Slot = (...args: any[]) => VNode[]
Expand Down Expand Up @@ -166,17 +174,15 @@ export declare interface ComponentInternalInstance {
isMounted: boolean
isUnmounted: boolean
isDeactivated: boolean
}

export function getCurrentVue2Instance() {
return currentInstance
/**
* @internal
*/
scope: EffectScope
}

export function getCurrentInstance() {
if (currentInstance) {
return toVue3ComponentInstance(currentInstance)
}
return null
return currentInstance
}

const instanceMapCache = new WeakMap<
Expand All @@ -203,6 +209,8 @@ function toVue3ComponentInstance(
root: null!, // to be immediately set
} as unknown as ComponentInternalInstance

bindCurrentScopeToVM(instance)

// map vm.$props =
const instanceProps = [
'data',
Expand Down
10 changes: 7 additions & 3 deletions src/utils/instance.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { ComponentInstance } from '../component'
import vmStateManager from './vmStateManager'
import { setCurrentInstance, getCurrentVue2Instance } from '../runtimeContext'
import {
setCurrentInstance,
getCurrentInstance,
setCurrentVue2Instance,
} from '../runtimeContext'
import { Ref, isRef, isReactive } from '../apis'
import { hasOwn, proxy, warn } from './utils'
import { createSlotProxy, resolveSlots } from './helper'
Expand Down Expand Up @@ -129,8 +133,8 @@ export function activateCurrentInstance(
fn: (vm_: ComponentInstance) => any,
onError?: (err: Error) => void
) {
let preVm = getCurrentVue2Instance()
setCurrentInstance(vm)
let preVm = getCurrentInstance()
setCurrentVue2Instance(vm)
try {
return fn(vm)
} catch (err) {
Expand Down
23 changes: 23 additions & 0 deletions test/v3/reactivity/effectScope.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
ref,
ComputedRef,
createApp,
getCurrentScope,
} from '../../../src'
import { mockWarn } from '../../helpers'

Expand Down Expand Up @@ -282,4 +283,26 @@ describe('reactivity/effect/scope', () => {
expect(dummy).toBe(7)
expect(doubled).toBe(14)
})

it('component should be a valid scope', async () => {
let dummy = 0
let scope

const root = document.createElement('div')
const vm = createApp({
setup() {
scope = getCurrentScope()
onScopeDispose(() => (dummy += 1))
scope?.cleanups.push(() => (dummy += 1))
},
})

vm.mount(root)
expect(dummy).toBe(0)
expect(scope).not.toBeFalsy()

vm.unmount()
await nextTick()
expect(dummy).toBe(2)
})
})

0 comments on commit da21873

Please sign in to comment.