fix(runtime-core): support `getCurrentInstance` across mutiple builds of Vue

This commit is contained in:
Evan You 2023-02-21 21:59:41 +08:00
parent f597dc69e7
commit 8d2d5bf48a
1 changed files with 63 additions and 3 deletions

View File

@ -56,7 +56,8 @@ import {
makeMap,
isPromise,
ShapeFlags,
extend
extend,
getGlobalThis
} from '@vue/shared'
import { SuspenseBoundary } from './components/Suspense'
import { CompilerOptions } from '@vue/compiler-core'
@ -565,14 +566,73 @@ export let currentInstance: ComponentInternalInstance | null = null
export const getCurrentInstance: () => ComponentInternalInstance | null = () =>
currentInstance || currentRenderingInstance
type GlobalInstanceSetter = ((
instance: ComponentInternalInstance | null
) => void) & { version?: string }
let globalCurrentInstanceSetters: GlobalInstanceSetter[]
let internalSetCurrentInstance: GlobalInstanceSetter
let hasWarnedDuplicatedVue = false
/**
* The following makes getCurrentInstance() usage across multiple copies of Vue
* work. Some cases of how this can happen are summarized in #7590. In principle
* the duplication should be avoided, but in practice there are often cases
* where the user is unable to resolve on their own, especially in complicated
* SSR setups.
*
* Note this fix is technically incomplete, as we still rely on other singletons
* for effectScope and global reactive dependency maps. However, it does make
* some of the most common cases work. It also warns if the duplication is
* found during browser execution.
*/
if (__SSR__) {
const settersKey = '__VUE_INSTANCE_SETTERS__'
if (!(globalCurrentInstanceSetters = getGlobalThis()[settersKey])) {
globalCurrentInstanceSetters = getGlobalThis()[settersKey] = []
}
globalCurrentInstanceSetters.push(i => (currentInstance = i))
if (__DEV__) {
globalCurrentInstanceSetters[
globalCurrentInstanceSetters.length - 1
].version = __VERSION__
}
internalSetCurrentInstance = instance => {
if (globalCurrentInstanceSetters.length > 1) {
// eslint-disable-next-line no-restricted-globals
if (__DEV__ && !hasWarnedDuplicatedVue && typeof window !== 'undefined') {
warn(
`Mixed usage of duplicated Vue runtimes detected: ${globalCurrentInstanceSetters
.map(fn => fn.version)
.join(', ')}.\n` +
`This likely means there are multiple versions of Vue ` +
`duplicated in your dependency tree, and could lead to errors. ` +
`To avoid this warning, ensure that the all imports of Vue are resolving to ` +
`the same location on disk.`
)
hasWarnedDuplicatedVue = true
}
globalCurrentInstanceSetters.forEach(s => s(instance))
} else {
globalCurrentInstanceSetters[0](instance)
}
}
} else {
internalSetCurrentInstance = i => {
currentInstance = i
}
}
export const setCurrentInstance = (instance: ComponentInternalInstance) => {
currentInstance = instance
internalSetCurrentInstance(instance)
instance.scope.on()
}
export const unsetCurrentInstance = () => {
currentInstance && currentInstance.scope.off()
currentInstance = null
internalSetCurrentInstance(null)
}
const isBuiltInTag = /*#__PURE__*/ makeMap('slot,component')