Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(ssr): refactor current rendering instance to stack for SSR #3461

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 12 additions & 7 deletions packages/runtime-core/__tests__/vnode.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ import { Data } from '../src/component'
import { ShapeFlags, PatchFlags } from '@vue/shared'
import { h, reactive, isReactive, setBlockTracking } from '../src'
import { createApp, nodeOps, serializeInner } from '@vue/runtime-test'
import { setCurrentRenderingInstance } from '../src/componentRenderContext'
import {
popRenderingInstance,
pushRenderingInstance
} from '../src/componentRenderContext'

describe('vnode', () => {
test('create with just tag', () => {
Expand Down Expand Up @@ -234,7 +237,7 @@ describe('vnode', () => {
const mockInstance1 = { type: {} } as any
const mockInstance2 = { type: {} } as any

setCurrentRenderingInstance(mockInstance1)
pushRenderingInstance(mockInstance1)
const original = createVNode('div', { ref: 'foo' })
expect(original.ref).toStrictEqual({ i: mockInstance1, r: 'foo' })

Expand All @@ -250,9 +253,10 @@ describe('vnode', () => {
const original2 = createVNode('div')
const cloned3 = cloneVNode(original2, { ref: 'bar' })
expect(cloned3.ref).toStrictEqual({ i: mockInstance1, r: 'bar' })
popRenderingInstance()

// cloning with different context instance
setCurrentRenderingInstance(mockInstance2)
pushRenderingInstance(mockInstance2)

// clone and preserve original ref
const cloned4 = cloneVNode(original)
Expand All @@ -268,26 +272,27 @@ describe('vnode', () => {
const cloned6 = cloneVNode(original2, { ref: 'bar' })
expect(cloned6.ref).toStrictEqual({ i: mockInstance2, r: 'bar' })

setCurrentRenderingInstance(null)
popRenderingInstance()
})

test('cloneVNode ref merging', () => {
const mockInstance1 = { type: {} } as any
const mockInstance2 = { type: {} } as any

setCurrentRenderingInstance(mockInstance1)
pushRenderingInstance(mockInstance1)
const original = createVNode('div', { ref: 'foo' })
expect(original.ref).toStrictEqual({ i: mockInstance1, r: 'foo' })
popRenderingInstance()

// clone and preserve original ref
setCurrentRenderingInstance(mockInstance2)
pushRenderingInstance(mockInstance2)
const cloned1 = cloneVNode(original, { ref: 'bar' }, true)
expect(cloned1.ref).toStrictEqual([
{ i: mockInstance1, r: 'foo' },
{ i: mockInstance2, r: 'bar' }
])

setCurrentRenderingInstance(null)
popRenderingInstance()
})

test('cloneVNode class normalization', () => {
Expand Down
20 changes: 13 additions & 7 deletions packages/runtime-core/src/componentRenderContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,19 @@ import { closeBlock, openBlock } from './vnode'
*/
export let currentRenderingInstance: ComponentInternalInstance | null = null
export let currentScopeId: string | null = null

export function setCurrentRenderingInstance(
instance: ComponentInternalInstance | null
) {
const currentRenderingInstanceStack: ComponentInternalInstance[] = []
HcySunYang marked this conversation as resolved.
Show resolved Hide resolved
export function pushRenderingInstance(instance: ComponentInternalInstance) {
currentRenderingInstance = instance
currentScopeId = (instance && instance.type.__scopeId) || null
currentRenderingInstanceStack.push(instance)
}
export function popRenderingInstance() {
currentRenderingInstanceStack.pop()
currentRenderingInstance =
currentRenderingInstanceStack[currentRenderingInstanceStack.length - 1]
currentScopeId =
(currentRenderingInstance && currentRenderingInstance.type.__scopeId) ||
null
}

/**
Expand All @@ -40,10 +47,9 @@ export function withCtx(
if (!isRenderingCompiledSlot) {
openBlock(true /* null block that disables tracking */)
}
const prevInstance = currentRenderingInstance
setCurrentRenderingInstance(ctx)
pushRenderingInstance(ctx)
const res = fn(...args)
setCurrentRenderingInstance(prevInstance)
popRenderingInstance()
if (!isRenderingCompiledSlot) {
closeBlock()
}
Expand Down
9 changes: 6 additions & 3 deletions packages/runtime-core/src/componentRenderUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@ import { warn } from './warning'
import { isHmrUpdating } from './hmr'
import { NormalizedProps } from './componentProps'
import { isEmitListener } from './componentEmits'
import { setCurrentRenderingInstance } from './componentRenderContext'
import {
pushRenderingInstance,
popRenderingInstance
} from './componentRenderContext'

/**
* dev only flag to track whether $attrs was used during render.
Expand Down Expand Up @@ -52,7 +55,7 @@ export function renderComponentRoot(
} = instance

let result
setCurrentRenderingInstance(instance)
pushRenderingInstance(instance)
if (__DEV__) {
accessedAttrs = false
}
Expand Down Expand Up @@ -205,7 +208,7 @@ export function renderComponentRoot(
result = createVNode(Comment)
}

setCurrentRenderingInstance(null)
popRenderingInstance()
return result
}

Expand Down
8 changes: 6 additions & 2 deletions packages/runtime-core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -257,14 +257,18 @@ export { transformVNodeArgs } from './vnode'

import { createComponentInstance, setupComponent } from './component'
import { renderComponentRoot } from './componentRenderUtils'
import { setCurrentRenderingInstance } from './componentRenderContext'
import {
pushRenderingInstance,
popRenderingInstance
} from './componentRenderContext'
import { isVNode, normalizeVNode } from './vnode'

const _ssrUtils = {
createComponentInstance,
setupComponent,
renderComponentRoot,
setCurrentRenderingInstance,
pushRenderingInstance,
popRenderingInstance,
isVNode,
normalizeVNode
}
Expand Down
34 changes: 33 additions & 1 deletion packages/server-renderer/__tests__/render.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,17 @@ import {
createStaticVNode,
KeepAlive,
withCtx,
Transition
Transition,
resolveDynamicComponent,
createVNode
} from 'vue'
import { escapeHtml } from '@vue/shared'
import { renderToString } from '../src/renderToString'
import { renderToStream as _renderToStream } from '../src/renderToStream'
import { ssrRenderSlot, SSRSlot } from '../src/helpers/ssrRenderSlot'
import { ssrRenderComponent } from '../src/helpers/ssrRenderComponent'
import { Readable } from 'stream'
import { ssrRenderVNode } from '../src'

const promisifyStream = (stream: Readable) => {
return new Promise<string>((resolve, reject) => {
Expand Down Expand Up @@ -311,6 +314,35 @@ function testRender(type: string, render: typeof renderToString) {
)
).toBe(`<div>parent<div>opt!</div><div>vnode!</div></div>`)
})

// #2863
test('assets should be resolved correctly', async () => {
expect(
await render(
createApp({
components: {
A: {
ssrRender(_ctx, _push) {
_push(`<div>A</div>`)
}
},
B: {
render: () => h('div', 'B')
}
},
ssrRender(_ctx, _push, _parent) {
const A: any = resolveComponent('A')
_push(ssrRenderComponent(A, null, null, _parent))
ssrRenderVNode(
_push,
createVNode(resolveDynamicComponent('B'), null, null),
_parent
)
}
})
)
).toBe(`<div>A</div><div>B</div>`)
})
})

describe('slots', () => {
Expand Down
7 changes: 4 additions & 3 deletions packages/server-renderer/src/render.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ import { ssrRenderTeleport } from './helpers/ssrRenderTeleport'

const {
createComponentInstance,
setCurrentRenderingInstance,
pushRenderingInstance,
popRenderingInstance,
setupComponent,
renderComponentRoot,
normalizeVNode
Expand Down Expand Up @@ -142,7 +143,7 @@ function renderComponentSubTree(
}

// set current rendering instance for asset resolution
setCurrentRenderingInstance(instance)
pushRenderingInstance(instance)
ssrRender(
instance.proxy,
push,
Expand All @@ -154,7 +155,7 @@ function renderComponentSubTree(
instance.data,
instance.ctx
)
setCurrentRenderingInstance(null)
popRenderingInstance()
} else if (instance.render) {
renderVNode(
push,
Expand Down