mirror of https://github.com/vuejs/core.git
feat(compiler/runtime-vapor): implement v-slots + v-for / v-if (#207)
Co-authored-by: Rizumu Ayaka <rizumu@ayaka.moe> Co-authored-by: 三咲智子 Kevin Deng <sxzz@sxzz.moe>
This commit is contained in:
parent
2e2f3e2b96
commit
4e13a57d9c
|
@ -17,6 +17,78 @@ export function render(_ctx) {
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`compiler: transform slot > dynamic slots name w/ v-for 1`] = `
|
||||||
|
"import { resolveComponent as _resolveComponent, createComponent as _createComponent, createForSlots as _createForSlots, template as _template } from 'vue/vapor';
|
||||||
|
const t0 = _template("foo")
|
||||||
|
|
||||||
|
export function render(_ctx) {
|
||||||
|
const _component_Comp = _resolveComponent("Comp")
|
||||||
|
const n2 = _createComponent(_component_Comp, null, null, () => [_createForSlots(_ctx.list, (item) => ({
|
||||||
|
name: item,
|
||||||
|
fn: () => {
|
||||||
|
const n0 = t0()
|
||||||
|
return n0
|
||||||
|
}
|
||||||
|
}))], true)
|
||||||
|
return n2
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`compiler: transform slot > dynamic slots name w/ v-for and provide absent key 1`] = `
|
||||||
|
"import { resolveComponent as _resolveComponent, createComponent as _createComponent, createForSlots as _createForSlots, template as _template } from 'vue/vapor';
|
||||||
|
const t0 = _template("foo")
|
||||||
|
|
||||||
|
export function render(_ctx) {
|
||||||
|
const _component_Comp = _resolveComponent("Comp")
|
||||||
|
const n2 = _createComponent(_component_Comp, null, null, () => [_createForSlots(_ctx.list, (_, __, index) => ({
|
||||||
|
name: index,
|
||||||
|
fn: () => {
|
||||||
|
const n0 = t0()
|
||||||
|
return n0
|
||||||
|
}
|
||||||
|
}))], true)
|
||||||
|
return n2
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`compiler: transform slot > dynamic slots name w/ v-if / v-else[-if] 1`] = `
|
||||||
|
"import { resolveComponent as _resolveComponent, createComponent as _createComponent, template as _template } from 'vue/vapor';
|
||||||
|
const t0 = _template("condition slot")
|
||||||
|
const t1 = _template("another condition")
|
||||||
|
const t2 = _template("else condition")
|
||||||
|
|
||||||
|
export function render(_ctx) {
|
||||||
|
const _component_Comp = _resolveComponent("Comp")
|
||||||
|
const n6 = _createComponent(_component_Comp, null, null, () => [_ctx.condition
|
||||||
|
? {
|
||||||
|
name: "condition",
|
||||||
|
fn: () => {
|
||||||
|
const n0 = t0()
|
||||||
|
return n0
|
||||||
|
},
|
||||||
|
key: "0"
|
||||||
|
}
|
||||||
|
: _ctx.anotherCondition
|
||||||
|
? {
|
||||||
|
name: "condition",
|
||||||
|
fn: () => {
|
||||||
|
const n2 = t1()
|
||||||
|
return n2
|
||||||
|
},
|
||||||
|
key: "1"
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
name: "condition",
|
||||||
|
fn: () => {
|
||||||
|
const n4 = t2()
|
||||||
|
return n4
|
||||||
|
},
|
||||||
|
key: "2"
|
||||||
|
}], true)
|
||||||
|
return n6
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`compiler: transform slot > implicit default slot 1`] = `
|
exports[`compiler: transform slot > implicit default slot 1`] = `
|
||||||
"import { resolveComponent as _resolveComponent, createComponent as _createComponent, template as _template } from 'vue/vapor';
|
"import { resolveComponent as _resolveComponent, createComponent as _createComponent, template as _template } from 'vue/vapor';
|
||||||
const t0 = _template("<div></div>")
|
const t0 = _template("<div></div>")
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { ErrorCodes, NodeTypes } from '@vue/compiler-core'
|
import { ErrorCodes, NodeTypes } from '@vue/compiler-core'
|
||||||
import {
|
import {
|
||||||
|
DynamicSlotType,
|
||||||
IRNodeTypes,
|
IRNodeTypes,
|
||||||
transformChildren,
|
transformChildren,
|
||||||
transformElement,
|
transformElement,
|
||||||
|
@ -126,6 +127,112 @@ describe('compiler: transform slot', () => {
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('dynamic slots name w/ v-for', () => {
|
||||||
|
const { ir, code } = compileWithSlots(
|
||||||
|
`<Comp>
|
||||||
|
<template v-for="item in list" #[item]>foo</template>
|
||||||
|
</Comp>`,
|
||||||
|
)
|
||||||
|
expect(code).toMatchSnapshot()
|
||||||
|
expect(ir.block.operation[0].type).toBe(IRNodeTypes.CREATE_COMPONENT_NODE)
|
||||||
|
expect(ir.block.operation).toMatchObject([
|
||||||
|
{
|
||||||
|
type: IRNodeTypes.CREATE_COMPONENT_NODE,
|
||||||
|
tag: 'Comp',
|
||||||
|
slots: undefined,
|
||||||
|
dynamicSlots: [
|
||||||
|
{
|
||||||
|
name: {
|
||||||
|
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||||
|
content: 'item',
|
||||||
|
isStatic: false,
|
||||||
|
},
|
||||||
|
fn: { type: IRNodeTypes.BLOCK },
|
||||||
|
loop: {
|
||||||
|
source: { content: 'list' },
|
||||||
|
value: { content: 'item' },
|
||||||
|
key: undefined,
|
||||||
|
index: undefined,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
test('dynamic slots name w/ v-for and provide absent key', () => {
|
||||||
|
const { ir, code } = compileWithSlots(
|
||||||
|
`<Comp>
|
||||||
|
<template v-for="(,,index) in list" #[index]>foo</template>
|
||||||
|
</Comp>`,
|
||||||
|
)
|
||||||
|
expect(code).toMatchSnapshot()
|
||||||
|
expect(ir.block.operation[0].type).toBe(IRNodeTypes.CREATE_COMPONENT_NODE)
|
||||||
|
expect(ir.block.operation).toMatchObject([
|
||||||
|
{
|
||||||
|
type: IRNodeTypes.CREATE_COMPONENT_NODE,
|
||||||
|
tag: 'Comp',
|
||||||
|
slots: undefined,
|
||||||
|
dynamicSlots: [
|
||||||
|
{
|
||||||
|
name: {
|
||||||
|
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||||
|
content: 'index',
|
||||||
|
isStatic: false,
|
||||||
|
},
|
||||||
|
fn: { type: IRNodeTypes.BLOCK },
|
||||||
|
loop: {
|
||||||
|
source: { content: 'list' },
|
||||||
|
value: undefined,
|
||||||
|
key: undefined,
|
||||||
|
index: {
|
||||||
|
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
test('dynamic slots name w/ v-if / v-else[-if]', () => {
|
||||||
|
const { ir, code } = compileWithSlots(
|
||||||
|
`<Comp>
|
||||||
|
<template v-if="condition" #condition>condition slot</template>
|
||||||
|
<template v-else-if="anotherCondition" #condition>another condition</template>
|
||||||
|
<template v-else #condition>else condition</template>
|
||||||
|
</Comp>`,
|
||||||
|
)
|
||||||
|
expect(code).toMatchSnapshot()
|
||||||
|
expect(ir.block.operation[0].type).toBe(IRNodeTypes.CREATE_COMPONENT_NODE)
|
||||||
|
expect(ir.block.operation).toMatchObject([
|
||||||
|
{
|
||||||
|
type: IRNodeTypes.CREATE_COMPONENT_NODE,
|
||||||
|
tag: 'Comp',
|
||||||
|
slots: undefined,
|
||||||
|
dynamicSlots: [
|
||||||
|
{
|
||||||
|
slotType: DynamicSlotType.CONDITIONAL,
|
||||||
|
condition: { content: 'condition' },
|
||||||
|
positive: {
|
||||||
|
slotType: DynamicSlotType.BASIC,
|
||||||
|
key: 0,
|
||||||
|
},
|
||||||
|
negative: {
|
||||||
|
slotType: DynamicSlotType.CONDITIONAL,
|
||||||
|
condition: { content: 'anotherCondition' },
|
||||||
|
positive: {
|
||||||
|
slotType: DynamicSlotType.BASIC,
|
||||||
|
key: 1,
|
||||||
|
},
|
||||||
|
negative: { slotType: DynamicSlotType.BASIC, key: 2 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
describe('errors', () => {
|
describe('errors', () => {
|
||||||
test('error on extraneous children w/ named default slot', () => {
|
test('error on extraneous children w/ named default slot', () => {
|
||||||
const onError = vi.fn()
|
const onError = vi.fn()
|
||||||
|
|
|
@ -1,9 +1,13 @@
|
||||||
import { camelize, extend, isArray } from '@vue/shared'
|
import { camelize, extend, isArray } from '@vue/shared'
|
||||||
import type { CodegenContext } from '../generate'
|
import type { CodegenContext } from '../generate'
|
||||||
import {
|
import {
|
||||||
|
type ComponentBasicDynamicSlot,
|
||||||
|
type ComponentConditionalDynamicSlot,
|
||||||
type ComponentDynamicSlot,
|
type ComponentDynamicSlot,
|
||||||
|
type ComponentLoopDynamicSlot,
|
||||||
type ComponentSlots,
|
type ComponentSlots,
|
||||||
type CreateComponentIRNode,
|
type CreateComponentIRNode,
|
||||||
|
DynamicSlotType,
|
||||||
IRDynamicPropsKind,
|
IRDynamicPropsKind,
|
||||||
type IRProp,
|
type IRProp,
|
||||||
type IRProps,
|
type IRProps,
|
||||||
|
@ -15,6 +19,8 @@ import {
|
||||||
DELIMITERS_ARRAY_NEWLINE,
|
DELIMITERS_ARRAY_NEWLINE,
|
||||||
DELIMITERS_OBJECT,
|
DELIMITERS_OBJECT,
|
||||||
DELIMITERS_OBJECT_NEWLINE,
|
DELIMITERS_OBJECT_NEWLINE,
|
||||||
|
INDENT_END,
|
||||||
|
INDENT_START,
|
||||||
NEWLINE,
|
NEWLINE,
|
||||||
genCall,
|
genCall,
|
||||||
genMulti,
|
genMulti,
|
||||||
|
@ -155,13 +161,90 @@ function genDynamicSlots(
|
||||||
) {
|
) {
|
||||||
const slotsExpr = genMulti(
|
const slotsExpr = genMulti(
|
||||||
dynamicSlots.length > 1 ? DELIMITERS_ARRAY_NEWLINE : DELIMITERS_ARRAY,
|
dynamicSlots.length > 1 ? DELIMITERS_ARRAY_NEWLINE : DELIMITERS_ARRAY,
|
||||||
...dynamicSlots.map(({ name, fn }) =>
|
...dynamicSlots.map(slot => genDynamicSlot(slot, context)),
|
||||||
genMulti(
|
|
||||||
DELIMITERS_OBJECT_NEWLINE,
|
|
||||||
['name: ', ...genExpression(name, context)],
|
|
||||||
['fn: ', ...genBlock(fn, context)],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
return ['() => ', ...slotsExpr]
|
return ['() => ', ...slotsExpr]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function genDynamicSlot(
|
||||||
|
slot: ComponentDynamicSlot,
|
||||||
|
context: CodegenContext,
|
||||||
|
): CodeFragment[] {
|
||||||
|
switch (slot.slotType) {
|
||||||
|
case DynamicSlotType.BASIC:
|
||||||
|
return genBasicDynamicSlot(slot, context)
|
||||||
|
case DynamicSlotType.LOOP:
|
||||||
|
return genLoopSlot(slot, context)
|
||||||
|
case DynamicSlotType.CONDITIONAL:
|
||||||
|
return genConditionalSlot(slot, context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function genBasicDynamicSlot(
|
||||||
|
slot: ComponentBasicDynamicSlot,
|
||||||
|
context: CodegenContext,
|
||||||
|
): CodeFragment[] {
|
||||||
|
const { name, fn, key } = slot
|
||||||
|
return genMulti(
|
||||||
|
DELIMITERS_OBJECT_NEWLINE,
|
||||||
|
['name: ', ...genExpression(name, context)],
|
||||||
|
['fn: ', ...genBlock(fn, context)],
|
||||||
|
...(key !== undefined ? [`key: "${key}"`] : []),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function genLoopSlot(
|
||||||
|
slot: ComponentLoopDynamicSlot,
|
||||||
|
context: CodegenContext,
|
||||||
|
): CodeFragment[] {
|
||||||
|
const { name, fn, loop } = slot
|
||||||
|
const { value, key, index, source } = loop
|
||||||
|
const rawValue = value && value.content
|
||||||
|
const rawKey = key && key.content
|
||||||
|
const rawIndex = index && index.content
|
||||||
|
|
||||||
|
const idMap: Record<string, string> = {}
|
||||||
|
if (rawValue) idMap[rawValue] = rawValue
|
||||||
|
if (rawKey) idMap[rawKey] = rawKey
|
||||||
|
if (rawIndex) idMap[rawIndex] = rawIndex
|
||||||
|
const slotExpr = genMulti(
|
||||||
|
DELIMITERS_OBJECT_NEWLINE,
|
||||||
|
['name: ', ...context.withId(() => genExpression(name, context), idMap)],
|
||||||
|
['fn: ', ...context.withId(() => genBlock(fn, context), idMap)],
|
||||||
|
)
|
||||||
|
return [
|
||||||
|
...genCall(
|
||||||
|
context.vaporHelper('createForSlots'),
|
||||||
|
genExpression(source, context),
|
||||||
|
[
|
||||||
|
...genMulti(
|
||||||
|
['(', ')', ', '],
|
||||||
|
rawValue ? rawValue : rawKey || rawIndex ? '_' : undefined,
|
||||||
|
rawKey ? rawKey : rawIndex ? '__' : undefined,
|
||||||
|
rawIndex,
|
||||||
|
),
|
||||||
|
' => (',
|
||||||
|
...slotExpr,
|
||||||
|
')',
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
function genConditionalSlot(
|
||||||
|
slot: ComponentConditionalDynamicSlot,
|
||||||
|
context: CodegenContext,
|
||||||
|
): CodeFragment[] {
|
||||||
|
const { condition, positive, negative } = slot
|
||||||
|
return [
|
||||||
|
...genExpression(condition, context),
|
||||||
|
INDENT_START,
|
||||||
|
NEWLINE,
|
||||||
|
'? ',
|
||||||
|
...genDynamicSlot(positive, context),
|
||||||
|
NEWLINE,
|
||||||
|
': ',
|
||||||
|
...(negative ? [...genDynamicSlot(negative, context)] : ['void 0']),
|
||||||
|
INDENT_END,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
|
@ -73,13 +73,16 @@ export interface IfIRNode extends BaseIRNode {
|
||||||
once?: boolean
|
once?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ForIRNode extends BaseIRNode {
|
export interface IRFor {
|
||||||
type: IRNodeTypes.FOR
|
|
||||||
id: number
|
|
||||||
source: SimpleExpressionNode
|
source: SimpleExpressionNode
|
||||||
value?: SimpleExpressionNode
|
value?: SimpleExpressionNode
|
||||||
key?: SimpleExpressionNode
|
key?: SimpleExpressionNode
|
||||||
index?: SimpleExpressionNode
|
index?: SimpleExpressionNode
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ForIRNode extends BaseIRNode, IRFor {
|
||||||
|
type: IRNodeTypes.FOR
|
||||||
|
id: number
|
||||||
keyProp?: SimpleExpressionNode
|
keyProp?: SimpleExpressionNode
|
||||||
render: BlockIRNode
|
render: BlockIRNode
|
||||||
once: boolean
|
once: boolean
|
||||||
|
@ -208,12 +211,39 @@ export interface ComponentSlotBlockIRNode extends BlockIRNode {
|
||||||
// TODO slot props
|
// TODO slot props
|
||||||
}
|
}
|
||||||
export type ComponentSlots = Record<string, ComponentSlotBlockIRNode>
|
export type ComponentSlots = Record<string, ComponentSlotBlockIRNode>
|
||||||
export interface ComponentDynamicSlot {
|
|
||||||
|
export enum DynamicSlotType {
|
||||||
|
BASIC,
|
||||||
|
LOOP,
|
||||||
|
CONDITIONAL,
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ComponentBasicDynamicSlot {
|
||||||
|
slotType: DynamicSlotType.BASIC
|
||||||
name: SimpleExpressionNode
|
name: SimpleExpressionNode
|
||||||
fn: ComponentSlotBlockIRNode
|
fn: ComponentSlotBlockIRNode
|
||||||
key?: string
|
key?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ComponentLoopDynamicSlot {
|
||||||
|
slotType: DynamicSlotType.LOOP
|
||||||
|
name: SimpleExpressionNode
|
||||||
|
fn: ComponentSlotBlockIRNode
|
||||||
|
loop: IRFor
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ComponentConditionalDynamicSlot {
|
||||||
|
slotType: DynamicSlotType.CONDITIONAL
|
||||||
|
condition: SimpleExpressionNode
|
||||||
|
positive: ComponentBasicDynamicSlot
|
||||||
|
negative?: ComponentBasicDynamicSlot | ComponentConditionalDynamicSlot
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ComponentDynamicSlot =
|
||||||
|
| ComponentBasicDynamicSlot
|
||||||
|
| ComponentLoopDynamicSlot
|
||||||
|
| ComponentConditionalDynamicSlot
|
||||||
|
|
||||||
export interface CreateComponentIRNode extends BaseIRNode {
|
export interface CreateComponentIRNode extends BaseIRNode {
|
||||||
type: IRNodeTypes.CREATE_COMPONENT_NODE
|
type: IRNodeTypes.CREATE_COMPONENT_NODE
|
||||||
id: number
|
id: number
|
||||||
|
|
|
@ -10,7 +10,15 @@ import {
|
||||||
} from '@vue/compiler-core'
|
} from '@vue/compiler-core'
|
||||||
import type { NodeTransform, TransformContext } from '../transform'
|
import type { NodeTransform, TransformContext } from '../transform'
|
||||||
import { newBlock } from './utils'
|
import { newBlock } from './utils'
|
||||||
import { type BlockIRNode, DynamicFlag, type VaporDirectiveNode } from '../ir'
|
import {
|
||||||
|
type BlockIRNode,
|
||||||
|
type ComponentBasicDynamicSlot,
|
||||||
|
type ComponentConditionalDynamicSlot,
|
||||||
|
DynamicFlag,
|
||||||
|
DynamicSlotType,
|
||||||
|
type IRFor,
|
||||||
|
type VaporDirectiveNode,
|
||||||
|
} from '../ir'
|
||||||
import { findDir, resolveExpression } from '../utils'
|
import { findDir, resolveExpression } from '../utils'
|
||||||
|
|
||||||
// TODO dynamic slots
|
// TODO dynamic slots
|
||||||
|
@ -69,6 +77,9 @@ export const transformVSlot: NodeTransform = (node, context) => {
|
||||||
|
|
||||||
context.dynamic.flags |= DynamicFlag.NON_TEMPLATE
|
context.dynamic.flags |= DynamicFlag.NON_TEMPLATE
|
||||||
|
|
||||||
|
const vFor = findDir(node, 'for')
|
||||||
|
const vIf = findDir(node, 'if')
|
||||||
|
const vElse = findDir(node, /^else(-if)?$/, true /* allowEmpty */)
|
||||||
const slots = context.slots!
|
const slots = context.slots!
|
||||||
const dynamicSlots = context.dynamicSlots!
|
const dynamicSlots = context.dynamicSlots!
|
||||||
|
|
||||||
|
@ -79,7 +90,7 @@ export const transformVSlot: NodeTransform = (node, context) => {
|
||||||
|
|
||||||
arg &&= resolveExpression(arg)
|
arg &&= resolveExpression(arg)
|
||||||
|
|
||||||
if (!arg || arg.isStatic) {
|
if ((!arg || arg.isStatic) && !vFor && !vIf && !vElse) {
|
||||||
const slotName = arg ? arg.content : 'default'
|
const slotName = arg ? arg.content : 'default'
|
||||||
|
|
||||||
if (slots[slotName]) {
|
if (slots[slotName]) {
|
||||||
|
@ -92,12 +103,75 @@ export const transformVSlot: NodeTransform = (node, context) => {
|
||||||
} else {
|
} else {
|
||||||
slots[slotName] = block
|
slots[slotName] = block
|
||||||
}
|
}
|
||||||
|
} else if (vIf) {
|
||||||
|
dynamicSlots.push({
|
||||||
|
slotType: DynamicSlotType.CONDITIONAL,
|
||||||
|
condition: vIf.exp!,
|
||||||
|
positive: {
|
||||||
|
slotType: DynamicSlotType.BASIC,
|
||||||
|
name: arg!,
|
||||||
|
fn: block,
|
||||||
|
key: 0,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
} else if (vElse) {
|
||||||
|
const vIfIR = dynamicSlots[dynamicSlots.length - 1]
|
||||||
|
if (vIfIR.slotType === DynamicSlotType.CONDITIONAL) {
|
||||||
|
let ifNode = vIfIR
|
||||||
|
while (
|
||||||
|
ifNode.negative &&
|
||||||
|
ifNode.negative.slotType === DynamicSlotType.CONDITIONAL
|
||||||
|
)
|
||||||
|
ifNode = ifNode.negative
|
||||||
|
const negative:
|
||||||
|
| ComponentBasicDynamicSlot
|
||||||
|
| ComponentConditionalDynamicSlot = vElse.exp
|
||||||
|
? {
|
||||||
|
slotType: DynamicSlotType.CONDITIONAL,
|
||||||
|
condition: vElse.exp,
|
||||||
|
positive: {
|
||||||
|
slotType: DynamicSlotType.BASIC,
|
||||||
|
name: arg!,
|
||||||
|
fn: block,
|
||||||
|
key: ifNode.positive.key! + 1,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
slotType: DynamicSlotType.BASIC,
|
||||||
|
name: arg!,
|
||||||
|
fn: block,
|
||||||
|
key: ifNode.positive.key! + 1,
|
||||||
|
}
|
||||||
|
ifNode.negative = negative
|
||||||
|
} else {
|
||||||
|
context.options.onError(
|
||||||
|
createCompilerError(ErrorCodes.X_V_ELSE_NO_ADJACENT_IF, vElse.loc),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else if (vFor) {
|
||||||
|
if (vFor.forParseResult) {
|
||||||
|
dynamicSlots.push({
|
||||||
|
slotType: DynamicSlotType.LOOP,
|
||||||
|
name: arg!,
|
||||||
|
fn: block,
|
||||||
|
loop: vFor.forParseResult as IRFor,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
context.options.onError(
|
||||||
|
createCompilerError(
|
||||||
|
ErrorCodes.X_V_FOR_MALFORMED_EXPRESSION,
|
||||||
|
vFor.loc,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
dynamicSlots.push({
|
dynamicSlots.push({
|
||||||
name: arg,
|
slotType: DynamicSlotType.BASIC,
|
||||||
|
name: arg!,
|
||||||
fn: block,
|
fn: block,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return () => onExit()
|
return () => onExit()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import { renderEffect } from './renderEffect'
|
||||||
import { type Block, type Fragment, fragmentKey } from './apiRender'
|
import { type Block, type Fragment, fragmentKey } from './apiRender'
|
||||||
import { warn } from './warning'
|
import { warn } from './warning'
|
||||||
import { componentKey } from './component'
|
import { componentKey } from './component'
|
||||||
|
import type { DynamicSlot } from './componentSlots'
|
||||||
|
|
||||||
interface ForBlock extends Fragment {
|
interface ForBlock extends Fragment {
|
||||||
scope: EffectScope
|
scope: EffectScope
|
||||||
|
@ -301,8 +302,22 @@ export const createFor = (
|
||||||
remove(nodes, parent!)
|
remove(nodes, parent!)
|
||||||
scope.stop()
|
scope.stop()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function getLength(source: any): number {
|
export function createForSlots(
|
||||||
|
source: any[] | Record<any, any> | number | Set<any> | Map<any, any>,
|
||||||
|
getSlot: (item: any, key: any, index?: number) => DynamicSlot,
|
||||||
|
): DynamicSlot[] {
|
||||||
|
const sourceLength = getLength(source)
|
||||||
|
const slots = new Array<DynamicSlot>(sourceLength)
|
||||||
|
for (let i = 0; i < sourceLength; i++) {
|
||||||
|
const [item, key, index] = getItem(source, i)
|
||||||
|
slots[i] = getSlot(item, key, index)
|
||||||
|
}
|
||||||
|
return slots
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLength(source: any): number {
|
||||||
if (isArray(source) || isString(source)) {
|
if (isArray(source) || isString(source)) {
|
||||||
return source.length
|
return source.length
|
||||||
} else if (typeof source === 'number') {
|
} else if (typeof source === 'number') {
|
||||||
|
@ -318,12 +333,12 @@ export const createFor = (
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
function getItem(
|
function getItem(
|
||||||
source: any,
|
source: any,
|
||||||
idx: number,
|
idx: number,
|
||||||
): [item: any, key: any, index?: number] {
|
): [item: any, key: any, index?: number] {
|
||||||
if (isArray(source) || isString(source)) {
|
if (isArray(source) || isString(source)) {
|
||||||
return [source[idx], idx, undefined]
|
return [source[idx], idx, undefined]
|
||||||
} else if (typeof source === 'number') {
|
} else if (typeof source === 'number') {
|
||||||
|
@ -338,7 +353,6 @@ export const createFor = (
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null!
|
return null!
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizeAnchor(node: Block): Node {
|
function normalizeAnchor(node: Block): Node {
|
||||||
|
|
|
@ -53,7 +53,8 @@ export function initSlots(
|
||||||
slots = shallowReactive(slots)
|
slots = shallowReactive(slots)
|
||||||
const dynamicSlotKeys: Record<string, true> = {}
|
const dynamicSlotKeys: Record<string, true> = {}
|
||||||
firstEffect(instance, () => {
|
firstEffect(instance, () => {
|
||||||
const _dynamicSlots = callWithAsyncErrorHandling(
|
const _dynamicSlots: (DynamicSlot | DynamicSlot[])[] =
|
||||||
|
callWithAsyncErrorHandling(
|
||||||
dynamicSlots,
|
dynamicSlots,
|
||||||
instance,
|
instance,
|
||||||
VaporErrorCodes.RENDER_FUNCTION,
|
VaporErrorCodes.RENDER_FUNCTION,
|
||||||
|
|
|
@ -126,7 +126,7 @@ export {
|
||||||
type FunctionPlugin,
|
type FunctionPlugin,
|
||||||
} from './apiCreateVaporApp'
|
} from './apiCreateVaporApp'
|
||||||
export { createIf } from './apiCreateIf'
|
export { createIf } from './apiCreateIf'
|
||||||
export { createFor } from './apiCreateFor'
|
export { createFor, createForSlots } from './apiCreateFor'
|
||||||
export { createComponent } from './apiCreateComponent'
|
export { createComponent } from './apiCreateComponent'
|
||||||
|
|
||||||
export { resolveComponent, resolveDirective } from './helpers/resolveAssets'
|
export { resolveComponent, resolveDirective } from './helpers/resolveAssets'
|
||||||
|
|
Loading…
Reference in New Issue