From 8b0dd0b4b4a5759e74f368aae05cc8349fd7610e Mon Sep 17 00:00:00 2001
From: chouchouji <1305974212@qq.com>
Date: Mon, 28 Jul 2025 16:30:41 +0800
Subject: [PATCH] =?UTF-8?q?fix(mp):=20=E4=BF=AE=E5=A4=8D=20v-if=20?=
=?UTF-8?q?=E5=92=8C=20v-slot=20=E5=90=8C=E6=97=B6=E4=BD=BF=E7=94=A8?=
=?UTF-8?q?=E6=97=B6=20v-if=20=E4=B8=8D=E8=B5=B7=E4=BD=9C=E7=94=A8?=
=?UTF-8?q?=E7=9A=84=20Bug?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../uni-mp-alipay/__tests__/vSlot.spec.ts | 4 +-
packages/uni-mp-baidu/__tests__/vSlot.spec.ts | 4 +-
.../uni-mp-compiler/__tests__/vSlot.spec.ts | 122 +++++++++++++++++-
.../uni-mp-compiler/src/transforms/vSlot.ts | 118 ++++++++++++++++-
.../uni-mp-core/src/runtime/componentProps.ts | 8 +-
5 files changed, 243 insertions(+), 13 deletions(-)
diff --git a/packages/uni-mp-alipay/__tests__/vSlot.spec.ts b/packages/uni-mp-alipay/__tests__/vSlot.spec.ts
index e3e377dc89d..6c878ecff0c 100644
--- a/packages/uni-mp-alipay/__tests__/vSlot.spec.ts
+++ b/packages/uni-mp-alipay/__tests__/vSlot.spec.ts
@@ -57,9 +57,9 @@ describe('mp-alipay: transform v-slot', () => {
test('v-if + scoped slots', () => {
assert(
`{{ slotProps.item }}`,
- `{{slotProps.a}}`,
+ `{{slotProps.a}}`,
`(_ctx, _cache) => {
- return _e({ a: _ctx.ok }, _ctx.ok ? { b: _w((slotProps, s0, i0) => { return { a: _t(slotProps.item), b: i0, c: s0 }; }, { name: 'd', path: 'b', vueId: '2a9ec0b0-0' }) } : {})
+ return _e({ a: _ctx.ok }, _ctx.ok ? { b: _w((slotProps, s0, i0) => { return { a: _t(slotProps.item), b: i0, c: s0 }; }, { name: 'd', path: 'b', vueId: '2a9ec0b0-0' }) } : {}, { c: [_ctx.ok ? 'd' : ''] })
}`
)
})
diff --git a/packages/uni-mp-baidu/__tests__/vSlot.spec.ts b/packages/uni-mp-baidu/__tests__/vSlot.spec.ts
index 2adbc2ea5c5..f0253fc2569 100644
--- a/packages/uni-mp-baidu/__tests__/vSlot.spec.ts
+++ b/packages/uni-mp-baidu/__tests__/vSlot.spec.ts
@@ -57,9 +57,9 @@ describe('compiler: transform v-slot', () => {
test('v-if + scoped slots', () => {
assert(
`{{ slotProps.item }}`,
- `{{slotProps.b}}`,
+ `{{slotProps.b}}`,
`(_ctx, _cache) => {
- return _e({ a: _ctx.ok }, _ctx.ok ? { b: _w((slotProps, s0, i0) => { return { a: i0, b: _t(slotProps.item) }; }, { name: 'd', path: 'b', vueId: '2a9ec0b0-0' }) } : {})
+ return _e({ a: _ctx.ok }, _ctx.ok ? { b: _w((slotProps, s0, i0) => { return { a: i0, b: _t(slotProps.item) }; }, { name: 'd', path: 'b', vueId: '2a9ec0b0-0' }) } : {}, { c: [_ctx.ok ? 'd' : ''] })
}`
)
})
diff --git a/packages/uni-mp-compiler/__tests__/vSlot.spec.ts b/packages/uni-mp-compiler/__tests__/vSlot.spec.ts
index 5e451fda84f..3b301a1ee5c 100644
--- a/packages/uni-mp-compiler/__tests__/vSlot.spec.ts
+++ b/packages/uni-mp-compiler/__tests__/vSlot.spec.ts
@@ -58,9 +58,9 @@ describe('compiler: transform v-slot', () => {
)
assert(
``,
- ``,
+ ``,
`(_ctx, _cache) => {
- return _e({ a: _ctx.ok }, _ctx.ok ? {} : {})
+ return _e({ a: _ctx.ok }, _ctx.ok ? {} : {}, { b: [_ctx.ok ? 'body' : ''] })
}`
)
})
@@ -88,9 +88,9 @@ describe('compiler: transform v-slot', () => {
test('v-if + scoped slots', () => {
assert(
`{{ slotProps.item }}`,
- `{{slotProps.a}}`,
+ `{{slotProps.a}}`,
`(_ctx, _cache) => {
- return _e({ a: _ctx.ok }, _ctx.ok ? { b: _w((slotProps, s0, i0) => { return { a: _t(slotProps.item), b: i0, c: s0 }; }, { name: 'd', path: 'b', vueId: '2a9ec0b0-0' }) } : {})
+ return _e({ a: _ctx.ok }, _ctx.ok ? { b: _w((slotProps, s0, i0) => { return { a: _t(slotProps.item), b: i0, c: s0 }; }, { name: 'd', path: 'b', vueId: '2a9ec0b0-0' }) } : {}, { c: [_ctx.ok ? 'd' : ''] })
}`
)
})
@@ -183,3 +183,117 @@ describe('should remove template when it has no any child nodes or all of its ch
}`
)
})
+
+describe('v-slot + v-if / v-else-if / v-else', () => {
+ assert(
+ `hello`,
+ `hello`,
+ `(_ctx, _cache) => {
+ return _e({ a: _ctx.a }, _ctx.a ? {} : {}, { b: [_ctx.a ? 'header' : ''] })
+}`
+ )
+
+ assert(
+ `hello`,
+ `hello`,
+ `(_ctx, _cache) => {
+ return _e({ a: _ctx.a }, _ctx.a ? {} : {}, { b: [_ctx.a ? 'd' : ''] })
+}`
+ )
+
+ assert(
+ `hellohello`,
+ `hellohello`,
+ `(_ctx, _cache) => {
+ return _e({ a: _ctx.a }, _ctx.a ? {} : {}, { b: [!_ctx.a ? 'footer' : '', _ctx.a ? 'd' : ''] })
+}`
+ )
+
+ assert(
+ `hellohello`,
+ `hellohello`,
+ `(_ctx, _cache) => {
+ return _e({ a: _ctx.a }, _ctx.a ? {} : {}, { b: _ctx.b }, _ctx.b ? {} : {}, { c: [_ctx.a ? 'header' : '', _ctx.b ? 'footer' : ''] })
+}`
+ )
+
+ assert(
+ `hellohello`,
+ `hellohello`,
+ `(_ctx, _cache) => {
+ return _e({ a: _ctx.a }, _ctx.a ? {} : {}, { b: _ctx.b }, _ctx.b ? {} : {}, { c: [_ctx.a ? 'header' : '', _ctx.b ? 'd' : ''] })
+}`
+ )
+
+ assert(
+ `hellohello`,
+ `hellohello`,
+ `(_ctx, _cache) => {
+ return _e({ a: _ctx.a }, _ctx.a ? {} : {}, { b: [_ctx.a ? 'header' : '', !_ctx.a ? 'footer' : ''] })
+}`
+ )
+
+ assert(
+ `hellohello`,
+ `hellohello`,
+ `(_ctx, _cache) => {
+ return _e({ a: _ctx.a }, _ctx.a ? {} : {}, { b: [_ctx.a ? 'header' : '', !_ctx.a ? 'd' : ''] })
+}`
+ )
+
+ assert(
+ `hellohello`,
+ `hellohello`,
+ `(_ctx, _cache) => {
+ return _e({ a: _ctx.a }, _ctx.a ? {} : _ctx.b ? {} : {}, { b: _ctx.b, c: [_ctx.a ? 'header' : '', !_ctx.a && _ctx.b ? 'footer' : ''] })
+}`
+ )
+
+ assert(
+ `hellohellohello`,
+ `hellohellohello`,
+ `(_ctx, _cache) => {
+ return _e({ a: _ctx.a }, _ctx.a ? {} : _ctx.b ? {} : _ctx.c ? {} : {}, { b: _ctx.b, c: _ctx.c, d: [_ctx.a ? 'header' : '', !_ctx.a && _ctx.b ? 'footer' : '', !_ctx.a && !_ctx.b && _ctx.c ? 'header2' : ''] })
+}`
+ )
+
+ assert(
+ `hellohellohello`,
+ `hellohellohello`,
+ `(_ctx, _cache) => {
+ return _e({ a: _ctx.a }, _ctx.a ? {} : _ctx.b ? {} : {}, { b: _ctx.b, c: _ctx.c }, _ctx.c ? {} : {}, { d: [_ctx.a ? 'header' : '', !_ctx.a && _ctx.b ? 'footer' : '', _ctx.c ? 'header2' : ''] })
+}`
+ )
+
+ assert(
+ `hellohellohello`,
+ `hellohellohello`,
+ `(_ctx, _cache) => {
+ return _e({ a: _ctx.a }, _ctx.a ? {} : _ctx.b ? {} : {}, { b: _ctx.b, c: [_ctx.a ? 'header' : '', !_ctx.a && _ctx.b ? 'footer' : '', !_ctx.a && !_ctx.b ? 'footer2' : ''] })
+}`
+ )
+
+ assert(
+ `hellohellohellohello`,
+ `hellohellohellohello`,
+ `(_ctx, _cache) => {
+ return _e({ a: _ctx.a }, _ctx.a ? {} : _ctx.b ? {} : _ctx.c ? {} : {}, { b: _ctx.b, c: _ctx.c, d: [_ctx.a ? 'header' : '', !_ctx.a && _ctx.b ? 'footer' : '', !_ctx.a && !_ctx.b && _ctx.c ? 'footer3' : '', !_ctx.a && !_ctx.b && !_ctx.c ? 'footer2' : ''] })
+}`
+ )
+
+ assert(
+ `hello`,
+ `hello`,
+ `(_ctx, _cache) => {
+ return _e({ a: _ctx.a }, _ctx.a ? { b: _d(_ctx.header) } : {}, { c: _d([_ctx.a ? _ctx.header : ""]) })
+}`
+ )
+
+ assert(
+ `hellohello`,
+ `hellohello`,
+ `(_ctx, _cache) => {
+ return _e({ a: _ctx.a }, _ctx.a ? { b: _d(_ctx.header) } : {}, { c: _d([_ctx.a ? _ctx.header : "", !_ctx.a ? "d" : ""]) })
+}`
+ )
+})
diff --git a/packages/uni-mp-compiler/src/transforms/vSlot.ts b/packages/uni-mp-compiler/src/transforms/vSlot.ts
index c5534d05bbf..db228450288 100644
--- a/packages/uni-mp-compiler/src/transforms/vSlot.ts
+++ b/packages/uni-mp-compiler/src/transforms/vSlot.ts
@@ -12,6 +12,7 @@ import {
type ComponentNode,
type CompoundExpressionNode,
type DirectiveNode,
+ type ElementNode,
ElementTypes,
ErrorCodes,
type ExpressionNode,
@@ -50,6 +51,13 @@ import {
} from './utils'
import { createVForArrowFunctionExpression } from './vFor'
import { DYNAMIC_SLOT } from '../runtimeHelpers'
+import { processExpression } from './transformExpression'
+
+type Condition = {
+ slotName: string | CompoundExpressionNode
+ condition: string
+ expression?: ExpressionNode
+}
export const transformSlot: NodeTransform = (node, context) => {
if (!isUserComponent(node, context as any)) {
@@ -58,6 +66,7 @@ export const transformSlot: NodeTransform = (node, context) => {
const { tag, children } = node
const slots = new Set()
+ const conditions: Condition[] = []
const onComponentSlot = findDir(node, 'slot', true)
const implicitDefaultChildren: TemplateChildNode[] = []
const isMiniProgramComponent = context.isMiniProgramComponent(tag)
@@ -83,6 +92,12 @@ export const transformSlot: NodeTransform = (node, context) => {
if (slotElement.type !== NodeTypes.COMMENT) {
implicitDefaultChildren.push(slotElement)
}
+ if (
+ slotElement.type === NodeTypes.ELEMENT &&
+ slotElement.tag === 'template'
+ ) {
+ buildConditions(slotElement, SLOT_DEFAULT_NAME, conditions)
+ }
continue
}
@@ -114,6 +129,9 @@ export const transformSlot: NodeTransform = (node, context) => {
if (slotName) {
slots.add(slotName)
+ if (slotElement.tag === 'template') {
+ buildConditions(slotElement, slotName, conditions)
+ }
}
}
@@ -136,6 +154,7 @@ export const transformSlot: NodeTransform = (node, context) => {
transformTemplateSlotElement(onComponentSlot, templateNode, node, context)
node.children = [templateNode]
}
+ const slotConditionMap = buildSlotConditionMap(conditions)
// 不支持 $slots, 则自动补充 props
if (slots.size && !context.miniProgram.slot.$slots) {
const slotsArr = [...slots]
@@ -145,10 +164,27 @@ export const transformSlot: NodeTransform = (node, context) => {
const children: (string | ExpressionNode)[] = []
const len = slotsArr.length - 1
slotsArr.forEach((name, index) => {
- if (isString(name)) {
- children.push(`'${dynamicSlotName(name)}'`)
+ if (slotConditionMap.has(name)) {
+ const slotName = isString(name)
+ ? createSimpleExpression(dynamicSlotName(name), true)
+ : name
+ children.push(
+ createCompoundExpression([
+ processExpression(
+ createSimpleExpression(
+ genExpr(slotConditionMap.get(name) as ExpressionNode),
+ false
+ ),
+ context
+ ),
+ ' ? ',
+ slotName,
+ ' : ',
+ createSimpleExpression('', true),
+ ])
+ )
} else {
- children.push(name)
+ children.push(isString(name) ? `'${dynamicSlotName(name)}'` : name)
}
if (index < len) {
children.push(',')
@@ -161,7 +197,12 @@ export const transformSlot: NodeTransform = (node, context) => {
])
} else {
value = `[${slotsArr
- .map((name) => `'${dynamicSlotName(name as string)}'`)
+ .map((name) => {
+ const slotName = dynamicSlotName(name as string)
+ return slotConditionMap.has(name)
+ ? `${genExpr(slotConditionMap.get(name))} ? '${slotName}' : ''`
+ : `'${slotName}'`
+ })
.join(',')}]`
}
node.props.unshift(createBindDirectiveNode(ATTR_VUE_SLOTS, value))
@@ -419,3 +460,72 @@ export function createVSlotCallExpression(
]),
])
}
+
+function buildConditions(
+ node: ElementNode,
+ slotName: string | CompoundExpressionNode,
+ conditions: Condition[]
+) {
+ const directives = [
+ { condition: 'if', hasExpr: true },
+ { condition: 'else-if', hasExpr: true },
+ { condition: 'else', hasExpr: false },
+ ]
+ for (const { condition, hasExpr } of directives) {
+ const dir = findDir(node, condition, true)
+ if (dir && (!hasExpr || dir.exp)) {
+ conditions.push({
+ slotName,
+ condition,
+ expression: hasExpr ? dir.exp : undefined,
+ })
+ return
+ }
+ }
+}
+
+function buildSlotConditionMap(conditions: Condition[]) {
+ const slotConditionMap = new Map()
+ const expressions: ExpressionNode[] = []
+ for (const condition of conditions) {
+ switch (condition.condition) {
+ case 'if':
+ // 直接设置为当前条件
+ slotConditionMap.set(condition.slotName, condition.expression!)
+ expressions.push(condition.expression!)
+ break
+ case 'else-if':
+ // else-if 前面的条件都取反,并当前条件
+ const expr = createCompoundExpression([
+ ...expressions
+ .map((expr, idx) => [
+ idx === 0 ? '' : ' && ',
+ createCompoundExpression(['!', '(', expr, ')']),
+ ])
+ .flat(),
+ ' && ',
+ condition.expression!,
+ ])
+ expressions.push(condition.expression!)
+ slotConditionMap.set(condition.slotName, expr)
+ break
+ case 'else':
+ // 条件都取反
+ slotConditionMap.set(
+ condition.slotName,
+ createCompoundExpression(
+ expressions
+ .map((expr, idx) => [
+ idx === 0 ? '' : ' && ',
+ createCompoundExpression(['!', '(', expr, ')']),
+ ])
+ .flat()
+ )
+ )
+ // 清空存储的 if/else-if 表达式
+ expressions.length = 0
+ break
+ }
+ }
+ return slotConditionMap
+}
diff --git a/packages/uni-mp-core/src/runtime/componentProps.ts b/packages/uni-mp-core/src/runtime/componentProps.ts
index dd5fc761d44..f6b46f806d5 100644
--- a/packages/uni-mp-core/src/runtime/componentProps.ts
+++ b/packages/uni-mp-core/src/runtime/componentProps.ts
@@ -48,11 +48,17 @@ function initDefaultProps(
const $slots = Object.create(null)
newVal &&
newVal.forEach((slotName: string) => {
- $slots[slotName] = true
+ if (slotName) {
+ $slots[slotName] = true
+ }
})
this.setData({
$slots,
})
+ if (this.$vm) {
+ this.$vm.$.slots = $slots
+ this.$vm.$forceUpdate()
+ }
}
properties.uS = {
type: null,