Skip to content

Commit

Permalink
refactor: fix implementation of SFC :slotted id handling
Browse files Browse the repository at this point in the history
fix #2892
  • Loading branch information
yyx990803 committed Mar 5, 2021
1 parent cc975c1 commit 13d0eb2
Show file tree
Hide file tree
Showing 36 changed files with 723 additions and 457 deletions.
51 changes: 19 additions & 32 deletions packages/compiler-core/__tests__/__snapshots__/scopeId.spec.ts.snap
Original file line number Diff line number Diff line change
@@ -1,91 +1,78 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`scopeId compiler support should push scopeId for hoisted nodes 1`] = `
"import { createVNode as _createVNode, toDisplayString as _toDisplayString, createTextVNode as _createTextVNode, openBlock as _openBlock, createBlock as _createBlock, withScopeId as _withScopeId, pushScopeId as _pushScopeId, popScopeId as _popScopeId } from \\"vue\\"
const _withId = /*#__PURE__*/_withScopeId(\\"test\\")
"import { createVNode as _createVNode, toDisplayString as _toDisplayString, createTextVNode as _createTextVNode, openBlock as _openBlock, createBlock as _createBlock, setScopeId as _setScopeId } from \\"vue\\"
_pushScopeId(\\"test\\")
_setScopeId(\\"test\\")
const _hoisted_1 = /*#__PURE__*/_createVNode(\\"div\\", null, \\"hello\\", -1 /* HOISTED */)
const _hoisted_2 = /*#__PURE__*/_createVNode(\\"div\\", null, \\"world\\", -1 /* HOISTED */)
_popScopeId()
_setScopeId(null)
export const render = /*#__PURE__*/_withId((_ctx, _cache) => {
export function render(_ctx, _cache) {
return (_openBlock(), _createBlock(\\"div\\", null, [
_hoisted_1,
_createTextVNode(_toDisplayString(_ctx.foo), 1 /* TEXT */),
_hoisted_2
]))
})"
}"
`;

exports[`scopeId compiler support should wrap default slot 1`] = `
"import { createVNode as _createVNode, resolveComponent as _resolveComponent, withCtx as _withCtx, openBlock as _openBlock, createBlock as _createBlock, withScopeId as _withScopeId } from \\"vue\\"
const _withId = /*#__PURE__*/_withScopeId(\\"test\\")
"import { createVNode as _createVNode, resolveComponent as _resolveComponent, withCtx as _withCtx, openBlock as _openBlock, createBlock as _createBlock } from \\"vue\\"
export const render = /*#__PURE__*/_withId((_ctx, _cache) => {
export function render(_ctx, _cache) {
const _component_Child = _resolveComponent(\\"Child\\")
return (_openBlock(), _createBlock(_component_Child, null, {
default: _withId(() => [
default: _withCtx(() => [
_createVNode(\\"div\\")
]),
_: 1 /* STABLE */
}))
})"
}"
`;

exports[`scopeId compiler support should wrap dynamic slots 1`] = `
"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 = /*#__PURE__*/_withScopeId(\\"test\\")
"import { createVNode as _createVNode, resolveComponent as _resolveComponent, withCtx as _withCtx, renderList as _renderList, createSlots as _createSlots, openBlock as _openBlock, createBlock as _createBlock } from \\"vue\\"
export const render = /*#__PURE__*/_withId((_ctx, _cache) => {
export function render(_ctx, _cache) {
const _component_Child = _resolveComponent(\\"Child\\")
return (_openBlock(), _createBlock(_component_Child, null, _createSlots({ _: 2 /* DYNAMIC */ }, [
(_ctx.ok)
? {
name: \\"foo\\",
fn: _withId(() => [
fn: _withCtx(() => [
_createVNode(\\"div\\")
])
}
: undefined,
_renderList(_ctx.list, (i) => {
return {
name: i,
fn: _withId(() => [
fn: _withCtx(() => [
_createVNode(\\"div\\")
])
}
})
]), 1024 /* DYNAMIC_SLOTS */))
})"
}"
`;

exports[`scopeId compiler support should wrap named slots 1`] = `
"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 = /*#__PURE__*/_withScopeId(\\"test\\")
"import { toDisplayString as _toDisplayString, createTextVNode as _createTextVNode, createVNode as _createVNode, resolveComponent as _resolveComponent, withCtx as _withCtx, openBlock as _openBlock, createBlock as _createBlock } from \\"vue\\"
export const render = /*#__PURE__*/_withId((_ctx, _cache) => {
export function render(_ctx, _cache) {
const _component_Child = _resolveComponent(\\"Child\\")
return (_openBlock(), _createBlock(_component_Child, null, {
foo: _withId(({ msg }) => [
foo: _withCtx(({ msg }) => [
_createTextVNode(_toDisplayString(msg), 1 /* TEXT */)
]),
bar: _withId(() => [
bar: _withCtx(() => [
_createVNode(\\"div\\")
]),
_: 1 /* STABLE */
}))
})"
`;

exports[`scopeId compiler support should wrap render function 1`] = `
"import { createVNode as _createVNode, openBlock as _openBlock, createBlock as _createBlock, withScopeId as _withScopeId } from \\"vue\\"
const _withId = /*#__PURE__*/_withScopeId(\\"test\\")
export const render = /*#__PURE__*/_withId((_ctx, _cache) => {
return (_openBlock(), _createBlock(\\"div\\"))
})"
}"
`;
41 changes: 14 additions & 27 deletions packages/compiler-core/__tests__/scopeId.spec.ts
Original file line number Diff line number Diff line change
@@ -1,38 +1,26 @@
import { baseCompile } from '../src/compile'
import {
WITH_SCOPE_ID,
PUSH_SCOPE_ID,
POP_SCOPE_ID
} from '../src/runtimeHelpers'
import { SET_SCOPE_ID } from '../src/runtimeHelpers'
import { PatchFlags } from '@vue/shared'
import { genFlagText } from './testUtils'

/**
* Ensure all slot functions are wrapped with _withCtx
* which sets the currentRenderingInstance and currentScopeId when rendering
* the slot.
*/
describe('scopeId compiler support', () => {
test('should only work in module mode', () => {
expect(() => {
baseCompile(``, { scopeId: 'test' })
}).toThrow(`"scopeId" option is only supported in module mode`)
})

test('should wrap render function', () => {
const { ast, code } = baseCompile(`<div/>`, {
mode: 'module',
scopeId: 'test'
})
expect(ast.helpers).toContain(WITH_SCOPE_ID)
expect(code).toMatch(`const _withId = /*#__PURE__*/_withScopeId("test")`)
expect(code).toMatch(
`export const render = /*#__PURE__*/_withId((_ctx, _cache) => {`
)
expect(code).toMatchSnapshot()
})

test('should wrap default slot', () => {
const { code } = baseCompile(`<Child><div/></Child>`, {
mode: 'module',
scopeId: 'test'
})
expect(code).toMatch(`default: _withId(() => [`)
expect(code).toMatch(`default: _withCtx(() => [`)
expect(code).toMatchSnapshot()
})

Expand All @@ -48,8 +36,8 @@ describe('scopeId compiler support', () => {
scopeId: 'test'
}
)
expect(code).toMatch(`foo: _withId(({ msg }) => [`)
expect(code).toMatch(`bar: _withId(() => [`)
expect(code).toMatch(`foo: _withCtx(({ msg }) => [`)
expect(code).toMatch(`bar: _withCtx(() => [`)
expect(code).toMatchSnapshot()
})

Expand All @@ -65,8 +53,8 @@ describe('scopeId compiler support', () => {
scopeId: 'test'
}
)
expect(code).toMatch(/name: "foo",\s+fn: _withId\(/)
expect(code).toMatch(/name: i,\s+fn: _withId\(/)
expect(code).toMatch(/name: "foo",\s+fn: _withCtx\(/)
expect(code).toMatch(/name: i,\s+fn: _withCtx\(/)
expect(code).toMatchSnapshot()
})

Expand All @@ -79,19 +67,18 @@ describe('scopeId compiler support', () => {
hoistStatic: true
}
)
expect(ast.helpers).toContain(PUSH_SCOPE_ID)
expect(ast.helpers).toContain(POP_SCOPE_ID)
expect(ast.helpers).toContain(SET_SCOPE_ID)
expect(ast.hoists.length).toBe(2)
expect(code).toMatch(
[
`_pushScopeId("test")`,
`_setScopeId("test")`,
`const _hoisted_1 = /*#__PURE__*/_createVNode("div", null, "hello", ${genFlagText(
PatchFlags.HOISTED
)})`,
`const _hoisted_2 = /*#__PURE__*/_createVNode("div", null, "world", ${genFlagText(
PatchFlags.HOISTED
)})`,
`_popScopeId()`
`_setScopeId(null)`
].join('\n')
)
expect(code).toMatchSnapshot()
Expand Down
60 changes: 16 additions & 44 deletions packages/compiler-core/src/codegen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,7 @@ import {
SET_BLOCK_TRACKING,
CREATE_COMMENT,
CREATE_TEXT,
PUSH_SCOPE_ID,
POP_SCOPE_ID,
WITH_SCOPE_ID,
SET_SCOPE_ID,
WITH_DIRECTIVES,
CREATE_BLOCK,
OPEN_BLOCK,
Expand Down Expand Up @@ -197,12 +195,11 @@ export function generate(
indent,
deindent,
newline,
scopeId,
ssr
} = context

const hasHelpers = ast.helpers.length > 0
const useWithBlock = !prefixIdentifiers && mode !== 'module'
const genScopeId = !__BROWSER__ && scopeId != null && mode === 'module'
const isSetupInlined = !__BROWSER__ && !!options.inline

// preambles
Expand All @@ -212,7 +209,7 @@ export function generate(
? createCodegenContext(ast, options)
: context
if (!__BROWSER__ && mode === 'module') {
genModulePreamble(ast, preambleContext, genScopeId, isSetupInlined)
genModulePreamble(ast, preambleContext, isSetupInlined)
} else {
genFunctionPreamble(ast, preambleContext)
}
Expand All @@ -229,14 +226,7 @@ export function generate(
? args.map(arg => `${arg}: any`).join(',')
: args.join(', ')

if (genScopeId) {
if (isSetupInlined) {
push(`${PURE_ANNOTATION}_withId(`)
} else {
push(`const ${functionName} = ${PURE_ANNOTATION}_withId(`)
}
}
if (isSetupInlined || genScopeId) {
if (isSetupInlined) {
push(`(${signature}) => {`)
} else {
push(`function ${functionName}(${signature}) {`)
Expand Down Expand Up @@ -301,10 +291,6 @@ export function generate(
deindent()
push(`}`)

if (genScopeId) {
push(`)`)
}

return {
ast,
code: context.code,
Expand Down Expand Up @@ -375,23 +361,20 @@ function genFunctionPreamble(ast: RootNode, context: CodegenContext) {
function genModulePreamble(
ast: RootNode,
context: CodegenContext,
genScopeId: boolean,
inline?: boolean
) {
const {
push,
helper,
newline,
scopeId,
optimizeImports,
runtimeModuleName
runtimeModuleName,
scopeId,
mode
} = context

if (genScopeId) {
ast.helpers.push(WITH_SCOPE_ID)
if (ast.hoists.length) {
ast.helpers.push(PUSH_SCOPE_ID, POP_SCOPE_ID)
}
const genScopeId = !__BROWSER__ && scopeId != null && mode === 'module'
if (genScopeId && ast.hoists.length) {
ast.helpers.push(SET_SCOPE_ID)
}

// generate import statements for helpers
Expand Down Expand Up @@ -434,13 +417,6 @@ function genModulePreamble(
newline()
}

if (genScopeId) {
push(
`const _withId = ${PURE_ANNOTATION}${helper(WITH_SCOPE_ID)}("${scopeId}")`
)
newline()
}

genHoists(ast.hoists, context)
newline()

Expand Down Expand Up @@ -480,7 +456,7 @@ function genHoists(hoists: (JSChildNode | null)[], context: CodegenContext) {
// push scope Id before initializing hoisted vnodes so that these vnodes
// get the proper scopeId as well.
if (genScopeId) {
push(`${helper(PUSH_SCOPE_ID)}("${scopeId}")`)
push(`${helper(SET_SCOPE_ID)}("${scopeId}")`)
newline()
}

Expand All @@ -493,7 +469,7 @@ function genHoists(hoists: (JSChildNode | null)[], context: CodegenContext) {
})

if (genScopeId) {
push(`${helper(POP_SCOPE_ID)}()`)
push(`${helper(SET_SCOPE_ID)}(null)`)
newline()
}
context.pure = false
Expand Down Expand Up @@ -817,15 +793,11 @@ function genFunctionExpression(
node: FunctionExpression,
context: CodegenContext
) {
const { push, indent, deindent, scopeId, mode } = context
const { push, indent, deindent } = context
const { params, returns, body, newline, isSlot } = node
// slot functions also need to push scopeId before rendering its content
const genScopeId =
!__BROWSER__ && isSlot && scopeId != null && mode !== 'function'

if (genScopeId) {
push(`_withId(`)
} else if (isSlot) {
if (isSlot) {
// wrap slot functions with owner context
push(`_${helperNameMap[WITH_CTX]}(`)
}
push(`(`, node)
Expand Down Expand Up @@ -855,7 +827,7 @@ function genFunctionExpression(
deindent()
push(`}`)
}
if (genScopeId || isSlot) {
if (isSlot) {
push(`)`)
}
}
Expand Down
8 changes: 2 additions & 6 deletions packages/compiler-core/src/runtimeHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,7 @@ export const CAMELIZE = Symbol(__DEV__ ? `camelize` : ``)
export const CAPITALIZE = Symbol(__DEV__ ? `capitalize` : ``)
export const TO_HANDLER_KEY = Symbol(__DEV__ ? `toHandlerKey` : ``)
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 SET_SCOPE_ID = Symbol(__DEV__ ? `setScopeId` : ``)
export const WITH_CTX = Symbol(__DEV__ ? `withCtx` : ``)
export const UNREF = Symbol(__DEV__ ? `unref` : ``)
export const IS_REF = Symbol(__DEV__ ? `isRef` : ``)
Expand Down Expand Up @@ -61,9 +59,7 @@ export const helperNameMap: any = {
[CAPITALIZE]: `capitalize`,
[TO_HANDLER_KEY]: `toHandlerKey`,
[SET_BLOCK_TRACKING]: `setBlockTracking`,
[PUSH_SCOPE_ID]: `pushScopeId`,
[POP_SCOPE_ID]: `popScopeId`,
[WITH_SCOPE_ID]: `withScopeId`,
[SET_SCOPE_ID]: `setScopeId`,
[WITH_CTX]: `withCtx`,
[UNREF]: `unref`,
[IS_REF]: `isRef`
Expand Down
Loading

0 comments on commit 13d0eb2

Please sign in to comment.