mirror of https://github.com/vuejs/core.git
fix(runtime-vapor): detach effect scope & component instance (#174)
This commit is contained in:
parent
e640ec6088
commit
b447aceac5
|
@ -14,13 +14,17 @@ import {
|
||||||
setCurrentInstance,
|
setCurrentInstance,
|
||||||
} from '../../src/component'
|
} from '../../src/component'
|
||||||
import { getMetadata, recordPropMetadata } from '../../src/componentMetadata'
|
import { getMetadata, recordPropMetadata } from '../../src/componentMetadata'
|
||||||
|
import { getCurrentScope } from '@vue/reactivity'
|
||||||
|
|
||||||
let removeComponentInstance = NOOP
|
let removeComponentInstance = NOOP
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
const reset = setCurrentInstance(
|
const instance = createComponentInstance((() => {}) as any, {})
|
||||||
createComponentInstance((() => {}) as any, {}),
|
const reset = setCurrentInstance(instance)
|
||||||
)
|
const prev = getCurrentScope()
|
||||||
|
instance.scope.on()
|
||||||
removeComponentInstance = () => {
|
removeComponentInstance = () => {
|
||||||
|
instance.scope.prevScope = prev
|
||||||
|
instance.scope.off()
|
||||||
reset()
|
reset()
|
||||||
removeComponentInstance = NOOP
|
removeComponentInstance = NOOP
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
import {
|
import {
|
||||||
|
EffectScope,
|
||||||
|
getCurrentScope,
|
||||||
nextTick,
|
nextTick,
|
||||||
onBeforeUpdate,
|
onBeforeUpdate,
|
||||||
onEffectCleanup,
|
onEffectCleanup,
|
||||||
|
@ -10,6 +12,10 @@ import {
|
||||||
watchPostEffect,
|
watchPostEffect,
|
||||||
watchSyncEffect,
|
watchSyncEffect,
|
||||||
} from '../src'
|
} from '../src'
|
||||||
|
import {
|
||||||
|
type ComponentInternalInstance,
|
||||||
|
currentInstance,
|
||||||
|
} from '../src/component'
|
||||||
import { makeRender } from './_utils'
|
import { makeRender } from './_utils'
|
||||||
|
|
||||||
const define = makeRender<any>()
|
const define = makeRender<any>()
|
||||||
|
@ -207,4 +213,27 @@ describe('renderEffect', () => {
|
||||||
'[Vue warn] Unhandled error during execution of updated',
|
'[Vue warn] Unhandled error during execution of updated',
|
||||||
).toHaveBeenWarned()
|
).toHaveBeenWarned()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('should be called with the current instance and current scope', async () => {
|
||||||
|
const source = ref(0)
|
||||||
|
const scope = new EffectScope()
|
||||||
|
let instanceSnap: ComponentInternalInstance | null = null
|
||||||
|
let scopeSnap: EffectScope | undefined = undefined
|
||||||
|
const { instance } = define(() => {
|
||||||
|
scope.run(() => {
|
||||||
|
renderEffect(() => {
|
||||||
|
instanceSnap = currentInstance
|
||||||
|
scopeSnap = getCurrentScope()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}).render()
|
||||||
|
|
||||||
|
expect(instanceSnap).toBe(instance)
|
||||||
|
expect(scopeSnap).toBe(scope)
|
||||||
|
|
||||||
|
source.value++
|
||||||
|
await nextTick()
|
||||||
|
expect(instanceSnap).toBe(instance)
|
||||||
|
expect(scopeSnap).toBe(scope)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -39,7 +39,9 @@ const injectHook = (
|
||||||
}
|
}
|
||||||
pauseTracking()
|
pauseTracking()
|
||||||
const reset = setCurrentInstance(target)
|
const reset = setCurrentInstance(target)
|
||||||
const res = callWithAsyncErrorHandling(hook, target, type, args)
|
const res = target.scope.run(() =>
|
||||||
|
callWithAsyncErrorHandling(hook, target, type, args),
|
||||||
|
)
|
||||||
reset()
|
reset()
|
||||||
resetTracking()
|
resetTracking()
|
||||||
return res
|
return res
|
||||||
|
|
|
@ -64,7 +64,9 @@ export function setupComponent(
|
||||||
instance.setupState = proxyRefs(stateOrNode)
|
instance.setupState = proxyRefs(stateOrNode)
|
||||||
}
|
}
|
||||||
if (!block && component.render) {
|
if (!block && component.render) {
|
||||||
|
pauseTracking()
|
||||||
block = component.render(instance.setupState)
|
block = component.render(instance.setupState)
|
||||||
|
resetTracking()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (block instanceof DocumentFragment) {
|
if (block instanceof DocumentFragment) {
|
||||||
|
|
|
@ -182,9 +182,7 @@ export const getCurrentInstance: () => ComponentInternalInstance | null = () =>
|
||||||
export const setCurrentInstance = (instance: ComponentInternalInstance) => {
|
export const setCurrentInstance = (instance: ComponentInternalInstance) => {
|
||||||
const prev = currentInstance
|
const prev = currentInstance
|
||||||
currentInstance = instance
|
currentInstance = instance
|
||||||
instance.scope.on()
|
|
||||||
return () => {
|
return () => {
|
||||||
instance.scope.off()
|
|
||||||
currentInstance = prev
|
currentInstance = prev
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ export function invokeLifecycle(
|
||||||
if (hooks) {
|
if (hooks) {
|
||||||
const fn = () => {
|
const fn = () => {
|
||||||
const reset = setCurrentInstance(instance)
|
const reset = setCurrentInstance(instance)
|
||||||
invokeArrayFns(hooks)
|
instance.scope.run(() => invokeArrayFns(hooks))
|
||||||
reset()
|
reset()
|
||||||
}
|
}
|
||||||
post ? queuePostRenderEffect(fn) : fn()
|
post ? queuePostRenderEffect(fn) : fn()
|
||||||
|
|
|
@ -212,7 +212,7 @@ function resolvePropValue(
|
||||||
// value = propsDefaults[key]
|
// value = propsDefaults[key]
|
||||||
// } else {
|
// } else {
|
||||||
const reset = setCurrentInstance(instance)
|
const reset = setCurrentInstance(instance)
|
||||||
value = defaultValue.call(null, props)
|
instance.scope.run(() => (value = defaultValue.call(null, props)))
|
||||||
reset()
|
reset()
|
||||||
// }
|
// }
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -3,6 +3,7 @@ import {
|
||||||
ReactiveEffect,
|
ReactiveEffect,
|
||||||
type SchedulerJob,
|
type SchedulerJob,
|
||||||
SchedulerJobFlags,
|
SchedulerJobFlags,
|
||||||
|
getCurrentScope,
|
||||||
} from '@vue/reactivity'
|
} from '@vue/reactivity'
|
||||||
import { invokeArrayFns } from '@vue/shared'
|
import { invokeArrayFns } from '@vue/shared'
|
||||||
import {
|
import {
|
||||||
|
@ -16,6 +17,7 @@ import { invokeDirectiveHook } from './directives'
|
||||||
|
|
||||||
export function renderEffect(cb: () => void) {
|
export function renderEffect(cb: () => void) {
|
||||||
const instance = getCurrentInstance()
|
const instance = getCurrentInstance()
|
||||||
|
const scope = getCurrentScope()
|
||||||
|
|
||||||
let effect: ReactiveEffect
|
let effect: ReactiveEffect
|
||||||
|
|
||||||
|
@ -53,11 +55,23 @@ export function renderEffect(cb: () => void) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
effect = new ReactiveEffect(() => {
|
if (scope) {
|
||||||
const reset = instance && setCurrentInstance(instance)
|
const baseCb = cb
|
||||||
callWithAsyncErrorHandling(cb, instance, VaporErrorCodes.RENDER_FUNCTION)
|
cb = () => scope.run(baseCb)
|
||||||
reset?.()
|
}
|
||||||
})
|
|
||||||
|
if (instance) {
|
||||||
|
const baseCb = cb
|
||||||
|
cb = () => {
|
||||||
|
const reset = setCurrentInstance(instance)
|
||||||
|
baseCb()
|
||||||
|
reset()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
effect = new ReactiveEffect(() =>
|
||||||
|
callWithAsyncErrorHandling(cb, instance, VaporErrorCodes.RENDER_FUNCTION),
|
||||||
|
)
|
||||||
|
|
||||||
effect.scheduler = () => {
|
effect.scheduler = () => {
|
||||||
if (instance) job.id = instance.uid
|
if (instance) job.id = instance.uid
|
||||||
|
|
Loading…
Reference in New Issue