Creating a vm in a detached EffectScope before any other vm is initialized breaks tracking of the activeEffectScope
Version
2.7.12
Reproduction link
Super slim repro on stackblitz
Steps to reproduce
Just open the stackblitz reproduction and check the console. You'll see:
[Vue warn]: onScopeDispose() is called when there is no active effect scope to be associated with.
In general though, you can cause the issue like so:
- Create a detached effect scope before creating a regular vue VM
- Do something in the run block of that scope (like lazily import a file with a store definition) that causes a Vue VM to be created
- Call
onScopeDispose()
What is expected?
The active scope tracking should be maintained, and not get "tricked" by not having an ancestor VM.
What is actually happening?
When setCurrentInstance(vm)
is called, it passes prev
as the vm
argument, which is null
. This leads to .off
being called on the detached EffectScope
that Vue 2.7 creates when a VM is initialized. This is not the detached scope that we created manually, mind you. And since .off
is not supposed to be called on a detached EffectScope
(it says this in explicitly in code comments), bad stuff happens and Vue's global activeEffectScope
becomes undefined
.
This doesn't currently happen in our app at runtime (though I think it easily could), it happens during our test suite. We try to be as lazy as possible in our test environment when it comes to the code that each test imports, because it substantially helps jest run time, especially when running individual test files. Because of this, we use the lazy
option of the commonjs
babel plugin. This means the Vuex store that would normally be instantiated before the detached effect scope ends up getting created after the detached effect scope. But I haven't seen anything in the Vue docs to say I shouldn't be able to create a detached effect scope before any other Vue VM, so I think this is a legitimate bug?
I'd be willing to work on a PR to fix this, but I'd need guidance, since my limited knowledge base of Vue's internals would probably mean any solutions I would try might break other things.
Update: Just adding a reference to related issue in Vue 3. Not sure how similar the internals of the effect scope implementation/tracking are, but right here @posva
makes a comment that "you would never call off
on a detached effect scope". But that does, indeed seem to be exactly what Vue 2.7 does. It seems like the buggy behavior the person who brought this up was worried about does indeed occur in some cases.