diff --git a/packages/compiler-core/__tests__/__snapshots__/scopeId.spec.ts.snap b/packages/compiler-core/__tests__/__snapshots__/scopeId.spec.ts.snap index 6f69b200..84f84e43 100644 --- a/packages/compiler-core/__tests__/__snapshots__/scopeId.spec.ts.snap +++ b/packages/compiler-core/__tests__/__snapshots__/scopeId.spec.ts.snap @@ -19,7 +19,7 @@ export const render = _withId(function render(_ctx, _cache) { `; exports[`scopeId compiler support should wrap default slot 1`] = ` -"import { createVNode as _createVNode, resolveComponent as _resolveComponent, openBlock as _openBlock, createBlock as _createBlock, withScopeId as _withScopeId } from \\"vue\\" +"import { createVNode as _createVNode, resolveComponent as _resolveComponent, withCtx as _withCtx, openBlock as _openBlock, createBlock as _createBlock, withScopeId as _withScopeId } from \\"vue\\" const _withId = _withScopeId(\\"test\\") export const render = _withId(function render(_ctx, _cache) { @@ -28,14 +28,14 @@ export const render = _withId(function render(_ctx, _cache) { return (_openBlock(), _createBlock(_component_Child, null, { default: _withId(() => [ _createVNode(\\"div\\") - ]), + ], _ctx), _: 1 })) })" `; exports[`scopeId compiler support should wrap dynamic slots 1`] = ` -"import { createVNode as _createVNode, resolveComponent as _resolveComponent, renderList as _renderList, createSlots as _createSlots, openBlock as _openBlock, createBlock as _createBlock, withScopeId as _withScopeId } from \\"vue\\" +"import { createVNode as _createVNode, resolveComponent as _resolveComponent, withCtx as _withCtx, renderList as _renderList, createSlots as _createSlots, openBlock as _openBlock, createBlock as _createBlock, withScopeId as _withScopeId } from \\"vue\\" const _withId = _withScopeId(\\"test\\") export const render = _withId(function render(_ctx, _cache) { @@ -47,7 +47,7 @@ export const render = _withId(function render(_ctx, _cache) { name: \\"foo\\", fn: _withId(() => [ _createVNode(\\"div\\") - ]) + ], _ctx) } : undefined, _renderList(_ctx.list, (i) => { @@ -55,7 +55,7 @@ export const render = _withId(function render(_ctx, _cache) { name: i, fn: _withId(() => [ _createVNode(\\"div\\") - ]) + ], _ctx) } }) ]), 1024 /* DYNAMIC_SLOTS */)) @@ -63,7 +63,7 @@ export const render = _withId(function render(_ctx, _cache) { `; exports[`scopeId compiler support should wrap named slots 1`] = ` -"import { toDisplayString as _toDisplayString, createTextVNode as _createTextVNode, createVNode as _createVNode, resolveComponent as _resolveComponent, openBlock as _openBlock, createBlock as _createBlock, withScopeId as _withScopeId } from \\"vue\\" +"import { toDisplayString as _toDisplayString, createTextVNode as _createTextVNode, createVNode as _createVNode, resolveComponent as _resolveComponent, withCtx as _withCtx, openBlock as _openBlock, createBlock as _createBlock, withScopeId as _withScopeId } from \\"vue\\" const _withId = _withScopeId(\\"test\\") export const render = _withId(function render(_ctx, _cache) { @@ -72,10 +72,10 @@ export const render = _withId(function render(_ctx, _cache) { return (_openBlock(), _createBlock(_component_Child, null, { foo: _withId(({ msg }) => [ _createTextVNode(_toDisplayString(msg), 1 /* TEXT */) - ]), + ], _ctx), bar: _withId(() => [ _createVNode(\\"div\\") - ]), + ], _ctx), _: 1 })) })" diff --git a/packages/compiler-core/__tests__/transforms/__snapshots__/hoistStatic.spec.ts.snap b/packages/compiler-core/__tests__/transforms/__snapshots__/hoistStatic.spec.ts.snap index 0d0ddd8c..a7fdc60a 100644 --- a/packages/compiler-core/__tests__/transforms/__snapshots__/hoistStatic.spec.ts.snap +++ b/packages/compiler-core/__tests__/transforms/__snapshots__/hoistStatic.spec.ts.snap @@ -239,14 +239,14 @@ exports[`compiler: hoistStatic transform prefixIdentifiers should NOT hoist expr return function render(_ctx, _cache) { with (_ctx) { - const { toDisplayString: _toDisplayString, createTextVNode: _createTextVNode, resolveComponent: _resolveComponent, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = _Vue + const { toDisplayString: _toDisplayString, createTextVNode: _createTextVNode, resolveComponent: _resolveComponent, withCtx: _withCtx, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = _Vue const _component_Comp = _resolveComponent(\\"Comp\\") return (_openBlock(), _createBlock(_component_Comp, null, { - default: ({ foo }) => [ + default: _withCtx(({ foo }) => [ _createTextVNode(_toDisplayString(_ctx.foo), 1 /* TEXT */) - ], + ], _ctx), _: 1 })) } diff --git a/packages/compiler-core/__tests__/transforms/__snapshots__/vSlot.spec.ts.snap b/packages/compiler-core/__tests__/transforms/__snapshots__/vSlot.spec.ts.snap index 6e3414a9..a1bdf074 100644 --- a/packages/compiler-core/__tests__/transforms/__snapshots__/vSlot.spec.ts.snap +++ b/packages/compiler-core/__tests__/transforms/__snapshots__/vSlot.spec.ts.snap @@ -1,36 +1,36 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`compiler: transform component slots dynamically named slots 1`] = ` -"const { toDisplayString: _toDisplayString, resolveComponent: _resolveComponent, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = Vue +"const { toDisplayString: _toDisplayString, resolveComponent: _resolveComponent, withCtx: _withCtx, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = Vue return function render(_ctx, _cache) { const _component_Comp = _resolveComponent(\\"Comp\\") return (_openBlock(), _createBlock(_component_Comp, null, { - [_ctx.one]: ({ foo }) => [_toDisplayString(foo), _toDisplayString(_ctx.bar)], - [_ctx.two]: ({ bar }) => [_toDisplayString(_ctx.foo), _toDisplayString(bar)], + [_ctx.one]: _withCtx(({ foo }) => [_toDisplayString(foo), _toDisplayString(_ctx.bar)], _ctx), + [_ctx.two]: _withCtx(({ bar }) => [_toDisplayString(_ctx.foo), _toDisplayString(bar)], _ctx), _: 1 }, 1024 /* DYNAMIC_SLOTS */)) }" `; exports[`compiler: transform component slots implicit default slot 1`] = ` -"const { createVNode: _createVNode, resolveComponent: _resolveComponent, openBlock: _openBlock, createBlock: _createBlock } = Vue +"const { createVNode: _createVNode, resolveComponent: _resolveComponent, withCtx: _withCtx, openBlock: _openBlock, createBlock: _createBlock } = Vue return function render(_ctx, _cache) { const _component_Comp = _resolveComponent(\\"Comp\\") return (_openBlock(), _createBlock(_component_Comp, null, { - default: () => [ + default: _withCtx(() => [ _createVNode(\\"div\\") - ], + ], _ctx), _: 1 })) }" `; exports[`compiler: transform component slots named slot with v-for w/ prefixIdentifiers: true 1`] = ` -"const { toDisplayString: _toDisplayString, resolveComponent: _resolveComponent, renderList: _renderList, createSlots: _createSlots, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = Vue +"const { toDisplayString: _toDisplayString, resolveComponent: _resolveComponent, withCtx: _withCtx, renderList: _renderList, createSlots: _createSlots, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = Vue return function render(_ctx, _cache) { const _component_Comp = _resolveComponent(\\"Comp\\") @@ -39,7 +39,7 @@ return function render(_ctx, _cache) { _renderList(_ctx.list, (name) => { return { name: name, - fn: () => [_toDisplayString(name)] + fn: _withCtx(() => [_toDisplayString(name)], _ctx) } }) ]), 1024 /* DYNAMIC_SLOTS */)) @@ -47,7 +47,7 @@ return function render(_ctx, _cache) { `; exports[`compiler: transform component slots named slot with v-if + prefixIdentifiers: true 1`] = ` -"const { toDisplayString: _toDisplayString, resolveComponent: _resolveComponent, createSlots: _createSlots, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = Vue +"const { toDisplayString: _toDisplayString, resolveComponent: _resolveComponent, withCtx: _withCtx, createSlots: _createSlots, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = Vue return function render(_ctx, _cache) { const _component_Comp = _resolveComponent(\\"Comp\\") @@ -56,7 +56,7 @@ return function render(_ctx, _cache) { (_ctx.ok) ? { name: \\"one\\", - fn: (props) => [_toDisplayString(props)] + fn: _withCtx((props) => [_toDisplayString(props)], _ctx) } : undefined ]), 1024 /* DYNAMIC_SLOTS */)) @@ -68,7 +68,7 @@ exports[`compiler: transform component slots named slot with v-if + v-else-if + return function render(_ctx, _cache) { with (_ctx) { - const { resolveComponent: _resolveComponent, createSlots: _createSlots, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = _Vue + const { resolveComponent: _resolveComponent, withCtx: _withCtx, createSlots: _createSlots, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = _Vue const _component_Comp = _resolveComponent(\\"Comp\\") @@ -76,16 +76,16 @@ return function render(_ctx, _cache) { ok ? { name: \\"one\\", - fn: () => [\\"foo\\"] + fn: _withCtx(() => [\\"foo\\"], _ctx) } : orNot ? { name: \\"two\\", - fn: (props) => [\\"bar\\"] + fn: _withCtx((props) => [\\"bar\\"], _ctx) } : { name: \\"one\\", - fn: () => [\\"baz\\"] + fn: _withCtx(() => [\\"baz\\"], _ctx) } ]), 1024 /* DYNAMIC_SLOTS */)) } @@ -97,7 +97,7 @@ exports[`compiler: transform component slots named slot with v-if 1`] = ` return function render(_ctx, _cache) { with (_ctx) { - const { resolveComponent: _resolveComponent, createSlots: _createSlots, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = _Vue + const { resolveComponent: _resolveComponent, withCtx: _withCtx, createSlots: _createSlots, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = _Vue const _component_Comp = _resolveComponent(\\"Comp\\") @@ -105,7 +105,7 @@ return function render(_ctx, _cache) { ok ? { name: \\"one\\", - fn: () => [\\"hello\\"] + fn: _withCtx(() => [\\"hello\\"], _ctx) } : undefined ]), 1024 /* DYNAMIC_SLOTS */)) @@ -114,14 +114,14 @@ return function render(_ctx, _cache) { `; exports[`compiler: transform component slots named slots 1`] = ` -"const { toDisplayString: _toDisplayString, resolveComponent: _resolveComponent, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = Vue +"const { toDisplayString: _toDisplayString, resolveComponent: _resolveComponent, withCtx: _withCtx, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = Vue return function render(_ctx, _cache) { const _component_Comp = _resolveComponent(\\"Comp\\") return (_openBlock(), _createBlock(_component_Comp, null, { - one: ({ foo }) => [_toDisplayString(foo), _toDisplayString(_ctx.bar)], - two: ({ bar }) => [_toDisplayString(_ctx.foo), _toDisplayString(bar)], + one: _withCtx(({ foo }) => [_toDisplayString(foo), _toDisplayString(_ctx.bar)], _ctx), + two: _withCtx(({ bar }) => [_toDisplayString(_ctx.foo), _toDisplayString(bar)], _ctx), _: 1 })) }" @@ -132,16 +132,16 @@ exports[`compiler: transform component slots named slots w/ implicit default slo return function render(_ctx, _cache) { with (_ctx) { - const { createVNode: _createVNode, resolveComponent: _resolveComponent, openBlock: _openBlock, createBlock: _createBlock } = _Vue + const { createVNode: _createVNode, resolveComponent: _resolveComponent, withCtx: _withCtx, openBlock: _openBlock, createBlock: _createBlock } = _Vue const _component_Comp = _resolveComponent(\\"Comp\\") return (_openBlock(), _createBlock(_component_Comp, null, { - one: () => [\\"foo\\"], - default: () => [ + one: _withCtx(() => [\\"foo\\"], _ctx), + default: _withCtx(() => [ \\"bar\\", _createVNode(\\"span\\") - ], + ], _ctx), _: 1 })) } @@ -149,36 +149,36 @@ return function render(_ctx, _cache) { `; exports[`compiler: transform component slots nested slots scoping 1`] = ` -"const { toDisplayString: _toDisplayString, resolveComponent: _resolveComponent, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = Vue +"const { toDisplayString: _toDisplayString, resolveComponent: _resolveComponent, withCtx: _withCtx, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = Vue return function render(_ctx, _cache) { const _component_Inner = _resolveComponent(\\"Inner\\") const _component_Comp = _resolveComponent(\\"Comp\\") return (_openBlock(), _createBlock(_component_Comp, null, { - default: ({ foo }) => [ + default: _withCtx(({ foo }) => [ _createVNode(_component_Inner, null, { - default: ({ bar }) => [_toDisplayString(foo), _toDisplayString(bar), _toDisplayString(_ctx.baz)], + default: _withCtx(({ bar }) => [_toDisplayString(foo), _toDisplayString(bar), _toDisplayString(_ctx.baz)], _ctx), _: 1 }, 1024 /* DYNAMIC_SLOTS */), \\" \\", _toDisplayString(foo), _toDisplayString(_ctx.bar), _toDisplayString(_ctx.baz) - ], + ], _ctx), _: 1 })) }" `; exports[`compiler: transform component slots on-component default slot 1`] = ` -"const { toDisplayString: _toDisplayString, resolveComponent: _resolveComponent, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = Vue +"const { toDisplayString: _toDisplayString, resolveComponent: _resolveComponent, withCtx: _withCtx, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = Vue return function render(_ctx, _cache) { const _component_Comp = _resolveComponent(\\"Comp\\") return (_openBlock(), _createBlock(_component_Comp, null, { - default: ({ foo }) => [_toDisplayString(foo), _toDisplayString(_ctx.bar)], + default: _withCtx(({ foo }) => [_toDisplayString(foo), _toDisplayString(_ctx.bar)], _ctx), _: 1 })) }" diff --git a/packages/compiler-core/src/codegen.ts b/packages/compiler-core/src/codegen.ts index 4d0b7298..b83d2f3f 100644 --- a/packages/compiler-core/src/codegen.ts +++ b/packages/compiler-core/src/codegen.ts @@ -768,6 +768,8 @@ function genFunctionExpression( if (genScopeId) { push(`_withId(`) + } else if (isSlot) { + push(`_withCtx(`) } push(`(`, node) if (isArray(params)) { @@ -796,8 +798,8 @@ function genFunctionExpression( deindent() push(`}`) } - if (genScopeId) { - push(`)`) + if (genScopeId || isSlot) { + push(`, _ctx)`) } } diff --git a/packages/compiler-core/src/runtimeHelpers.ts b/packages/compiler-core/src/runtimeHelpers.ts index 11267c92..39409a6b 100644 --- a/packages/compiler-core/src/runtimeHelpers.ts +++ b/packages/compiler-core/src/runtimeHelpers.ts @@ -26,6 +26,7 @@ export const SET_BLOCK_TRACKING = Symbol(__DEV__ ? `setBlockTracking` : ``) export const PUSH_SCOPE_ID = Symbol(__DEV__ ? `pushScopeId` : ``) export const POP_SCOPE_ID = Symbol(__DEV__ ? `popScopeId` : ``) export const WITH_SCOPE_ID = Symbol(__DEV__ ? `withScopeId` : ``) +export const WITH_CTX = Symbol(__DEV__ ? `withCtx` : ``) // Name mapping for runtime helpers that need to be imported from 'vue' in // generated code. Make sure these are correctly exported in the runtime! @@ -56,7 +57,8 @@ export const helperNameMap: any = { [SET_BLOCK_TRACKING]: `setBlockTracking`, [PUSH_SCOPE_ID]: `pushScopeId`, [POP_SCOPE_ID]: `popScopeId`, - [WITH_SCOPE_ID]: `withScopeId` + [WITH_SCOPE_ID]: `withScopeId`, + [WITH_CTX]: `withCtx` } export function registerRuntimeHelpers(helpers: any) { diff --git a/packages/compiler-core/src/transforms/vSlot.ts b/packages/compiler-core/src/transforms/vSlot.ts index 0207cd7c..d02cda50 100644 --- a/packages/compiler-core/src/transforms/vSlot.ts +++ b/packages/compiler-core/src/transforms/vSlot.ts @@ -25,7 +25,7 @@ import { import { TransformContext, NodeTransform } from '../transform' import { createCompilerError, ErrorCodes } from '../errors' import { findDir, isTemplateNode, assert, isVSlot, hasScopeRef } from '../utils' -import { CREATE_SLOTS, RENDER_LIST } from '../runtimeHelpers' +import { CREATE_SLOTS, RENDER_LIST, WITH_CTX } from '../runtimeHelpers' import { parseForExpression, createForLoopParams } from './vFor' const isStaticExp = (p: JSChildNode): p is SimpleExpressionNode => @@ -119,6 +119,8 @@ export function buildSlots( slots: SlotsExpression hasDynamicSlots: boolean } { + context.helper(WITH_CTX) + const { children, loc } = node const slotsProperties: Property[] = [] const dynamicSlots: (ConditionalExpression | CallExpression)[] = [] diff --git a/packages/compiler-ssr/__tests__/ssrComponent.spec.ts b/packages/compiler-ssr/__tests__/ssrComponent.spec.ts index d4df8538..52acbfab 100644 --- a/packages/compiler-ssr/__tests__/ssrComponent.spec.ts +++ b/packages/compiler-ssr/__tests__/ssrComponent.spec.ts @@ -3,7 +3,7 @@ import { compile } from '../src' describe('ssr: components', () => { test('basic', () => { expect(compile(``).code).toMatchInlineSnapshot(` - "const { resolveComponent: _resolveComponent } = require(\\"vue\\") + "const { resolveComponent: _resolveComponent, withCtx: _withCtx } = require(\\"vue\\") const { ssrRenderComponent: _ssrRenderComponent } = require(\\"@vue/server-renderer\\") return function ssrRender(_ctx, _push, _parent) { @@ -20,7 +20,7 @@ describe('ssr: components', () => { test('dynamic component', () => { expect(compile(``).code) .toMatchInlineSnapshot(` - "const { resolveComponent: _resolveComponent } = require(\\"vue\\") + "const { resolveComponent: _resolveComponent, withCtx: _withCtx } = require(\\"vue\\") const { ssrRenderComponent: _ssrRenderComponent } = require(\\"@vue/server-renderer\\") return function ssrRender(_ctx, _push, _parent) { @@ -32,7 +32,7 @@ describe('ssr: components', () => { expect(compile(``).code) .toMatchInlineSnapshot(` - "const { resolveDynamicComponent: _resolveDynamicComponent } = require(\\"vue\\") + "const { resolveDynamicComponent: _resolveDynamicComponent, withCtx: _withCtx } = require(\\"vue\\") const { ssrRenderComponent: _ssrRenderComponent } = require(\\"@vue/server-renderer\\") return function ssrRender(_ctx, _push, _parent) { @@ -44,14 +44,14 @@ describe('ssr: components', () => { describe('slots', () => { test('implicit default slot', () => { expect(compile(`hello
`).code).toMatchInlineSnapshot(` - "const { resolveComponent: _resolveComponent, createVNode: _createVNode, createTextVNode: _createTextVNode } = require(\\"vue\\") + "const { resolveComponent: _resolveComponent, withCtx: _withCtx, createVNode: _createVNode, createTextVNode: _createTextVNode } = require(\\"vue\\") const { ssrRenderComponent: _ssrRenderComponent } = require(\\"@vue/server-renderer\\") return function ssrRender(_ctx, _push, _parent) { const _component_foo = _resolveComponent(\\"foo\\") _push(_ssrRenderComponent(_component_foo, null, { - default: (_, _push, _parent, _scopeId) => { + default: _withCtx((_, _push, _parent, _scopeId) => { if (_push) { _push(\`hello
\`) } else { @@ -60,7 +60,7 @@ describe('ssr: components', () => { _createVNode(\\"div\\") ] } - }, + }, _ctx), _: 1 }, _parent)) }" @@ -70,14 +70,14 @@ describe('ssr: components', () => { test('explicit default slot', () => { expect(compile(`{{ msg + outer }}`).code) .toMatchInlineSnapshot(` - "const { resolveComponent: _resolveComponent, toDisplayString: _toDisplayString, createTextVNode: _createTextVNode } = require(\\"vue\\") + "const { resolveComponent: _resolveComponent, withCtx: _withCtx, toDisplayString: _toDisplayString, createTextVNode: _createTextVNode } = require(\\"vue\\") const { ssrRenderComponent: _ssrRenderComponent, ssrInterpolate: _ssrInterpolate } = require(\\"@vue/server-renderer\\") return function ssrRender(_ctx, _push, _parent) { const _component_foo = _resolveComponent(\\"foo\\") _push(_ssrRenderComponent(_component_foo, null, { - default: ({ msg }, _push, _parent, _scopeId) => { + default: _withCtx(({ msg }, _push, _parent, _scopeId) => { if (_push) { _push(\`\${_ssrInterpolate(msg + _ctx.outer)}\`) } else { @@ -85,7 +85,7 @@ describe('ssr: components', () => { _createTextVNode(_toDisplayString(msg + _ctx.outer), 1 /* TEXT */) ] } - }, + }, _ctx), _: 1 }, _parent)) }" @@ -99,14 +99,14 @@ describe('ssr: components', () => {
`).code ).toMatchInlineSnapshot(` - "const { resolveComponent: _resolveComponent, createTextVNode: _createTextVNode } = require(\\"vue\\") + "const { resolveComponent: _resolveComponent, withCtx: _withCtx, createTextVNode: _createTextVNode } = require(\\"vue\\") const { ssrRenderComponent: _ssrRenderComponent } = require(\\"@vue/server-renderer\\") return function ssrRender(_ctx, _push, _parent) { const _component_foo = _resolveComponent(\\"foo\\") _push(_ssrRenderComponent(_component_foo, null, { - default: (_, _push, _parent, _scopeId) => { + default: _withCtx((_, _push, _parent, _scopeId) => { if (_push) { _push(\`foo\`) } else { @@ -114,8 +114,8 @@ describe('ssr: components', () => { _createTextVNode(\\"foo\\") ] } - }, - named: (_, _push, _parent, _scopeId) => { + }, _ctx), + named: _withCtx((_, _push, _parent, _scopeId) => { if (_push) { _push(\`bar\`) } else { @@ -123,7 +123,7 @@ describe('ssr: components', () => { _createTextVNode(\\"bar\\") ] } - }, + }, _ctx), _: 1 }, _parent)) }" @@ -136,7 +136,7 @@ describe('ssr: components', () => {
`).code ).toMatchInlineSnapshot(` - "const { resolveComponent: _resolveComponent, createTextVNode: _createTextVNode, createSlots: _createSlots } = require(\\"vue\\") + "const { resolveComponent: _resolveComponent, withCtx: _withCtx, createTextVNode: _createTextVNode, createSlots: _createSlots } = require(\\"vue\\") const { ssrRenderComponent: _ssrRenderComponent } = require(\\"@vue/server-renderer\\") return function ssrRender(_ctx, _push, _parent) { @@ -146,7 +146,7 @@ describe('ssr: components', () => { (_ctx.ok) ? { name: \\"named\\", - fn: (_, _push, _parent, _scopeId) => { + fn: _withCtx((_, _push, _parent, _scopeId) => { if (_push) { _push(\`foo\`) } else { @@ -154,7 +154,7 @@ describe('ssr: components', () => { _createTextVNode(\\"foo\\") ] } - } + }, _ctx) } : undefined ]), _parent)) @@ -168,7 +168,7 @@ describe('ssr: components', () => { `).code ).toMatchInlineSnapshot(` - "const { resolveComponent: _resolveComponent, toDisplayString: _toDisplayString, createTextVNode: _createTextVNode, renderList: _renderList, createSlots: _createSlots } = require(\\"vue\\") + "const { resolveComponent: _resolveComponent, withCtx: _withCtx, toDisplayString: _toDisplayString, createTextVNode: _createTextVNode, renderList: _renderList, createSlots: _createSlots } = require(\\"vue\\") const { ssrRenderComponent: _ssrRenderComponent, ssrInterpolate: _ssrInterpolate } = require(\\"@vue/server-renderer\\") return function ssrRender(_ctx, _push, _parent) { @@ -178,7 +178,7 @@ describe('ssr: components', () => { _renderList(_ctx.names, (key) => { return { name: key, - fn: ({ msg }, _push, _parent, _scopeId) => { + fn: _withCtx(({ msg }, _push, _parent, _scopeId) => { if (_push) { _push(\`\${_ssrInterpolate(msg + key + _ctx.bar)}\`) } else { @@ -186,7 +186,7 @@ describe('ssr: components', () => { _createTextVNode(_toDisplayString(msg + _ctx.key + _ctx.bar), 1 /* TEXT */) ] } - } + }, _ctx) } }) ]), _parent)) @@ -209,14 +209,14 @@ describe('ssr: components', () => { `).code ).toMatchInlineSnapshot(` - "const { resolveComponent: _resolveComponent, renderList: _renderList, Fragment: _Fragment, openBlock: _openBlock, createBlock: _createBlock, createVNode: _createVNode, createCommentVNode: _createCommentVNode } = require(\\"vue\\") + "const { resolveComponent: _resolveComponent, withCtx: _withCtx, renderList: _renderList, Fragment: _Fragment, openBlock: _openBlock, createBlock: _createBlock, createVNode: _createVNode, createCommentVNode: _createCommentVNode } = require(\\"vue\\") const { ssrRenderComponent: _ssrRenderComponent, ssrRenderList: _ssrRenderList } = require(\\"@vue/server-renderer\\") return function ssrRender(_ctx, _push, _parent) { const _component_foo = _resolveComponent(\\"foo\\") _push(_ssrRenderComponent(_component_foo, null, { - foo: ({ list }, _push, _parent, _scopeId) => { + foo: _withCtx(({ list }, _push, _parent, _scopeId) => { if (_push) { if (_ctx.ok) { _push(\`\`) @@ -238,8 +238,8 @@ describe('ssr: components', () => { : _createCommentVNode(\\"v-if\\", true) ] } - }, - bar: ({ ok }, _push, _parent, _scopeId) => { + }, _ctx), + bar: _withCtx(({ ok }, _push, _parent, _scopeId) => { if (_push) { if (ok) { _push(\`\`) @@ -261,7 +261,7 @@ describe('ssr: components', () => { : _createCommentVNode(\\"v-if\\", true) ] } - }, + }, _ctx), _: 1 }, _parent)) }" @@ -287,7 +287,7 @@ describe('ssr: components', () => { expect(compile(``).code) .toMatchInlineSnapshot(` - "const { resolveComponent: _resolveComponent } = require(\\"vue\\") + "const { resolveComponent: _resolveComponent, withCtx: _withCtx } = require(\\"vue\\") const { ssrRenderComponent: _ssrRenderComponent } = require(\\"@vue/server-renderer\\") return function ssrRender(_ctx, _push, _parent) { diff --git a/packages/compiler-ssr/__tests__/ssrScopeId.spec.ts b/packages/compiler-ssr/__tests__/ssrScopeId.spec.ts index 8cb8c1ec..fe9671b8 100644 --- a/packages/compiler-ssr/__tests__/ssrScopeId.spec.ts +++ b/packages/compiler-ssr/__tests__/ssrScopeId.spec.ts @@ -23,14 +23,14 @@ describe('ssr: scopeId', () => { scopeId }).code ).toMatchInlineSnapshot(` - "const { resolveComponent: _resolveComponent, createTextVNode: _createTextVNode } = require(\\"vue\\") + "const { resolveComponent: _resolveComponent, withCtx: _withCtx, createTextVNode: _createTextVNode } = require(\\"vue\\") const { ssrRenderComponent: _ssrRenderComponent } = require(\\"@vue/server-renderer\\") return function ssrRender(_ctx, _push, _parent) { const _component_foo = _resolveComponent(\\"foo\\") _push(_ssrRenderComponent(_component_foo, null, { - default: (_, _push, _parent, _scopeId) => { + default: _withCtx((_, _push, _parent, _scopeId) => { if (_push) { _push(\`foo\`) } else { @@ -38,7 +38,7 @@ describe('ssr: scopeId', () => { _createTextVNode(\\"foo\\") ] } - }, + }, _ctx), _: 1 }, _parent)) }" @@ -51,14 +51,14 @@ describe('ssr: scopeId', () => { scopeId }).code ).toMatchInlineSnapshot(` - "const { resolveComponent: _resolveComponent, createVNode: _createVNode } = require(\\"vue\\") + "const { resolveComponent: _resolveComponent, withCtx: _withCtx, createVNode: _createVNode } = require(\\"vue\\") const { ssrRenderComponent: _ssrRenderComponent } = require(\\"@vue/server-renderer\\") return function ssrRender(_ctx, _push, _parent) { const _component_foo = _resolveComponent(\\"foo\\") _push(_ssrRenderComponent(_component_foo, null, { - default: (_, _push, _parent, _scopeId) => { + default: _withCtx((_, _push, _parent, _scopeId) => { if (_push) { _push(\`hello\`) } else { @@ -66,7 +66,7 @@ describe('ssr: scopeId', () => { _createVNode(\\"span\\", null, \\"hello\\") ] } - }, + }, _ctx), _: 1 }, _parent)) }" @@ -79,7 +79,7 @@ describe('ssr: scopeId', () => { scopeId }).code ).toMatchInlineSnapshot(` - "const { resolveComponent: _resolveComponent, createVNode: _createVNode } = require(\\"vue\\") + "const { resolveComponent: _resolveComponent, withCtx: _withCtx, createVNode: _createVNode } = require(\\"vue\\") const { ssrRenderComponent: _ssrRenderComponent } = require(\\"@vue/server-renderer\\") return function ssrRender(_ctx, _push, _parent) { @@ -87,11 +87,11 @@ describe('ssr: scopeId', () => { const _component_bar = _resolveComponent(\\"bar\\") _push(_ssrRenderComponent(_component_foo, null, { - default: (_, _push, _parent, _scopeId) => { + default: _withCtx((_, _push, _parent, _scopeId) => { if (_push) { _push(\`hello\`) _push(_ssrRenderComponent(_component_bar, null, { - default: (_, _push, _parent, _scopeId) => { + default: _withCtx((_, _push, _parent, _scopeId) => { if (_push) { _push(\`\`) } else { @@ -99,21 +99,21 @@ describe('ssr: scopeId', () => { _createVNode(\\"span\\") ] } - }, + }, _ctx), _: 1 }, _parent)) } else { return [ _createVNode(\\"span\\", null, \\"hello\\"), _createVNode(_component_bar, null, { - default: () => [ + default: _withCtx(() => [ _createVNode(\\"span\\") - ], + ], _ctx), _: 1 }) ] } - }, + }, _ctx), _: 1 }, _parent)) }" diff --git a/packages/compiler-ssr/__tests__/ssrSuspense.spec.ts b/packages/compiler-ssr/__tests__/ssrSuspense.spec.ts index 7f38f513..83a75cf9 100644 --- a/packages/compiler-ssr/__tests__/ssrSuspense.spec.ts +++ b/packages/compiler-ssr/__tests__/ssrSuspense.spec.ts @@ -3,7 +3,7 @@ import { compile } from '../src' describe('ssr compile: suspense', () => { test('implicit default', () => { expect(compile(``).code).toMatchInlineSnapshot(` - "const { resolveComponent: _resolveComponent } = require(\\"vue\\") + "const { resolveComponent: _resolveComponent, withCtx: _withCtx } = require(\\"vue\\") const { ssrRenderComponent: _ssrRenderComponent, ssrRenderSuspense: _ssrRenderSuspense } = require(\\"@vue/server-renderer\\") return function ssrRender(_ctx, _push, _parent) { @@ -30,7 +30,7 @@ describe('ssr compile: suspense', () => { `).code ).toMatchInlineSnapshot(` - "const { resolveComponent: _resolveComponent } = require(\\"vue\\") + "const { resolveComponent: _resolveComponent, withCtx: _withCtx } = require(\\"vue\\") const { ssrRenderComponent: _ssrRenderComponent, ssrRenderSuspense: _ssrRenderSuspense } = require(\\"@vue/server-renderer\\") return function ssrRender(_ctx, _push, _parent) { diff --git a/packages/runtime-core/src/componentRenderUtils.ts b/packages/runtime-core/src/componentRenderUtils.ts index 1130c9b7..52356592 100644 --- a/packages/runtime-core/src/componentRenderUtils.ts +++ b/packages/runtime-core/src/componentRenderUtils.ts @@ -18,7 +18,6 @@ import { warn } from './warning' // resolveComponent, resolveDirective) during render export let currentRenderingInstance: ComponentInternalInstance | null = null -// exposed for server-renderer only export function setCurrentRenderingInstance( instance: ComponentInternalInstance | null ) { diff --git a/packages/runtime-core/src/helpers/scopeId.ts b/packages/runtime-core/src/helpers/scopeId.ts index 32c7c99a..a8ee2401 100644 --- a/packages/runtime-core/src/helpers/scopeId.ts +++ b/packages/runtime-core/src/helpers/scopeId.ts @@ -2,6 +2,9 @@ // These are only used in esm-bundler builds, but since exports cannot be // conditional, we can only drop inner implementations in non-bundler builds. +import { ComponentInternalInstance } from '../component' +import { withCtx } from './withRenderContext' + export let currentScopeId: string | null = null const scopeIdStack: string[] = [] @@ -20,13 +23,14 @@ export function popScopeId() { export function withScopeId(id: string): (fn: T) => T { if (__BUNDLER__) { - return ((fn: Function) => { - return function(this: any) { + return ((fn: Function, ctx?: ComponentInternalInstance) => { + function renderWithId(this: any) { pushScopeId(id) const res = fn.apply(this, arguments) popScopeId() return res } + return ctx ? withCtx(renderWithId, ctx) : renderWithId }) as any } else { return undefined as any diff --git a/packages/runtime-core/src/helpers/withRenderContext.ts b/packages/runtime-core/src/helpers/withRenderContext.ts new file mode 100644 index 00000000..47ed8091 --- /dev/null +++ b/packages/runtime-core/src/helpers/withRenderContext.ts @@ -0,0 +1,16 @@ +import { Slot } from '../componentSlots' +import { ComponentInternalInstance } from '../component' +import { + setCurrentRenderingInstance, + currentRenderingInstance +} from '../componentRenderUtils' + +export function withCtx(fn: Slot, ctx: ComponentInternalInstance) { + return function renderFnWithContext() { + const owner = currentRenderingInstance + setCurrentRenderingInstance(ctx) + const res = fn.apply(null, arguments) + setCurrentRenderingInstance(owner) + return res + } +} diff --git a/packages/runtime-core/src/index.ts b/packages/runtime-core/src/index.ts index 4fbf060e..b05acf09 100644 --- a/packages/runtime-core/src/index.ts +++ b/packages/runtime-core/src/index.ts @@ -85,6 +85,7 @@ export { // For compiler generated code // should sync with '@vue/compiler-core/src/runtimeConstants.ts' +export { withCtx } from './helpers/withRenderContext' export { withDirectives } from './directives' export { resolveComponent,