fix(ssr): make isInSSRComponentSetup state sharable across copies of Vue

This also fixes the issue when rendering async components in
the SFC playground with SSR enabled.
This commit is contained in:
Evan You 2023-12-12 21:34:40 +08:00
parent b010fcfc5c
commit e04d821422
1 changed files with 30 additions and 17 deletions

View File

@ -630,13 +630,10 @@ export let currentInstance: ComponentInternalInstance | null = null
export const getCurrentInstance: () => ComponentInternalInstance | null = () => export const getCurrentInstance: () => ComponentInternalInstance | null = () =>
currentInstance || currentRenderingInstance currentInstance || currentRenderingInstance
type GlobalInstanceSetter = (( let internalSetCurrentInstance: (
instance: ComponentInternalInstance | null instance: ComponentInternalInstance | null
) => void) & { version?: string } ) => void
let setInSSRSetupState: (state: boolean) => void
let internalSetCurrentInstance: GlobalInstanceSetter
let globalCurrentInstanceSetters: GlobalInstanceSetter[]
let settersKey = '__VUE_INSTANCE_SETTERS__'
/** /**
* The following makes getCurrentInstance() usage across multiple copies of Vue * The following makes getCurrentInstance() usage across multiple copies of Vue
@ -651,21 +648,36 @@ let settersKey = '__VUE_INSTANCE_SETTERS__'
* found during browser execution. * found during browser execution.
*/ */
if (__SSR__) { if (__SSR__) {
if (!(globalCurrentInstanceSetters = getGlobalThis()[settersKey])) { type Setter = (v: any) => void
globalCurrentInstanceSetters = getGlobalThis()[settersKey] = [] const g = getGlobalThis()
} const registerGlobalSetter = (key: string, setter: Setter) => {
globalCurrentInstanceSetters.push(i => (currentInstance = i)) let setters: Setter[]
internalSetCurrentInstance = instance => { if (!(setters = g[key])) setters = g[key] = []
if (globalCurrentInstanceSetters.length > 1) { setters.push(setter)
globalCurrentInstanceSetters.forEach(s => s(instance)) return (v: any) => {
} else { if (setters.length > 1) setters.forEach(set => set(v))
globalCurrentInstanceSetters[0](instance) else setters[0](v)
} }
} }
internalSetCurrentInstance = registerGlobalSetter(
`__VUE_INSTANCE_SETTERS__`,
v => (currentInstance = v)
)
// also make `isInSSRComponentSetup` sharable across copies of Vue.
// this is needed in the SFC playground when SSRing async components, since
// we have to load both the runtime and the server-renderer from CDNs, they
// contain duplicated copies of Vue runtime code.
setInSSRSetupState = registerGlobalSetter(
`__VUE_SSR_SETTERS__`,
v => (isInSSRComponentSetup = v)
)
} else { } else {
internalSetCurrentInstance = i => { internalSetCurrentInstance = i => {
currentInstance = i currentInstance = i
} }
setInSSRSetupState = v => {
isInSSRComponentSetup = v
}
} }
export const setCurrentInstance = (instance: ComponentInternalInstance) => { export const setCurrentInstance = (instance: ComponentInternalInstance) => {
@ -699,7 +711,7 @@ export function setupComponent(
instance: ComponentInternalInstance, instance: ComponentInternalInstance,
isSSR = false isSSR = false
) { ) {
isInSSRComponentSetup = isSSR isSSR && setInSSRSetupState(isSSR)
const { props, children } = instance.vnode const { props, children } = instance.vnode
const isStateful = isStatefulComponent(instance) const isStateful = isStatefulComponent(instance)
@ -709,7 +721,8 @@ export function setupComponent(
const setupResult = isStateful const setupResult = isStateful
? setupStatefulComponent(instance, isSSR) ? setupStatefulComponent(instance, isSSR)
: undefined : undefined
isInSSRComponentSetup = false
isSSR && setInSSRSetupState(false)
return setupResult return setupResult
} }