Skip to content

Commit 8b0dd0b

Browse files
committed
fix(mp): 修复 v-if 和 v-slot 同时使用时 v-if 不起作用的 Bug
1 parent 7aa53bc commit 8b0dd0b

File tree

5 files changed

+243
-13
lines changed

5 files changed

+243
-13
lines changed

packages/uni-mp-alipay/__tests__/vSlot.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,9 @@ describe('mp-alipay: transform v-slot', () => {
5757
test('v-if + scoped slots', () => {
5858
assert(
5959
`<custom><template v-if="ok" v-slot:default="slotProps"><view>{{ slotProps.item }}</view></template></custom>`,
60-
`<custom u-s="{{['d']}}" u-i="2a9ec0b0-0" onVI="__l"><block a:if="{{a}}"><view a:for="{{b}}" a:for-item="slotProps" a:key="b" slot="{{slotProps.c}}"><view>{{slotProps.a}}</view></view></block></custom>`,
60+
`<custom u-s="{{c}}" u-i="2a9ec0b0-0" onVI="__l"><block a:if="{{a}}"><view a:for="{{b}}" a:for-item="slotProps" a:key="b" slot="{{slotProps.c}}"><view>{{slotProps.a}}</view></view></block></custom>`,
6161
`(_ctx, _cache) => {
62-
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' }) } : {})
62+
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' : ''] })
6363
}`
6464
)
6565
})

packages/uni-mp-baidu/__tests__/vSlot.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,9 @@ describe('compiler: transform v-slot', () => {
5757
test('v-if + scoped slots', () => {
5858
assert(
5959
`<custom><template v-if="ok" v-slot:default="slotProps"><view>{{ slotProps.item }}</view></template></custom>`,
60-
`<custom u-s="{{['d']}}" u-i="2a9ec0b0-0"><view s-if="{{a}}"><block s-for="slotProps in b trackBy slotProps.a"><view>{{slotProps.b}}</view></block></view></custom>`,
60+
`<custom u-s="{{c}}" u-i="2a9ec0b0-0"><view s-if="{{a}}"><block s-for="slotProps in b trackBy slotProps.a"><view>{{slotProps.b}}</view></block></view></custom>`,
6161
`(_ctx, _cache) => {
62-
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' }) } : {})
62+
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' : ''] })
6363
}`
6464
)
6565
})

packages/uni-mp-compiler/__tests__/vSlot.spec.ts

Lines changed: 118 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,9 @@ describe('compiler: transform v-slot', () => {
5858
)
5959
assert(
6060
`<uni-list-item class="item"><template v-slot:body v-if="ok"><view class="item"></view></template></uni-list-item>`,
61-
`<uni-list-item u-s="{{['body']}}" class="item" u-i="2a9ec0b0-0"><view wx:if="{{a}}" class="item" slot="body"></view></uni-list-item>`,
61+
`<uni-list-item u-s="{{b}}" class="item" u-i="2a9ec0b0-0"><view wx:if="{{a}}" class="item" slot="body"></view></uni-list-item>`,
6262
`(_ctx, _cache) => {
63-
return _e({ a: _ctx.ok }, _ctx.ok ? {} : {})
63+
return _e({ a: _ctx.ok }, _ctx.ok ? {} : {}, { b: [_ctx.ok ? 'body' : ''] })
6464
}`
6565
)
6666
})
@@ -88,9 +88,9 @@ describe('compiler: transform v-slot', () => {
8888
test('v-if + scoped slots', () => {
8989
assert(
9090
`<custom><template v-if="ok" v-slot:default="slotProps"><view>{{ slotProps.item }}</view></template></custom>`,
91-
`<custom u-s="{{['d']}}" u-i="2a9ec0b0-0"><block wx:if="{{a}}"><view wx:for="{{b}}" wx:for-item="slotProps" wx:key="b" slot="{{slotProps.c}}"><view>{{slotProps.a}}</view></view></block></custom>`,
91+
`<custom u-s="{{c}}" u-i="2a9ec0b0-0"><block wx:if="{{a}}"><view wx:for="{{b}}" wx:for-item="slotProps" wx:key="b" slot="{{slotProps.c}}"><view>{{slotProps.a}}</view></view></block></custom>`,
9292
`(_ctx, _cache) => {
93-
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' }) } : {})
93+
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' : ''] })
9494
}`
9595
)
9696
})
@@ -183,3 +183,117 @@ describe('should remove template when it has no any child nodes or all of its ch
183183
}`
184184
)
185185
})
186+
187+
describe('v-slot + v-if / v-else-if / v-else', () => {
188+
assert(
189+
`<custom><template v-if="a" #header>hello</template></custom>`,
190+
`<custom u-s="{{b}}" u-i="2a9ec0b0-0"><view wx:if="{{a}}" slot="header">hello</view></custom>`,
191+
`(_ctx, _cache) => {
192+
return _e({ a: _ctx.a }, _ctx.a ? {} : {}, { b: [_ctx.a ? 'header' : ''] })
193+
}`
194+
)
195+
196+
assert(
197+
`<custom><template v-if="a">hello</template></custom>`,
198+
`<custom u-s="{{b}}" u-i="2a9ec0b0-0"><block wx:if="{{a}}">hello</block></custom>`,
199+
`(_ctx, _cache) => {
200+
return _e({ a: _ctx.a }, _ctx.a ? {} : {}, { b: [_ctx.a ? 'd' : ''] })
201+
}`
202+
)
203+
204+
assert(
205+
`<custom><template v-if="a">hello</template><template v-else #footer>hello</template></custom>`,
206+
`<custom u-s="{{b}}" u-i="2a9ec0b0-0"><block wx:if="{{a}}">hello</block><view wx:else slot="footer">hello</view></custom>`,
207+
`(_ctx, _cache) => {
208+
return _e({ a: _ctx.a }, _ctx.a ? {} : {}, { b: [!_ctx.a ? 'footer' : '', _ctx.a ? 'd' : ''] })
209+
}`
210+
)
211+
212+
assert(
213+
`<custom><template v-if="a" #header>hello</template><template v-if="b" #footer>hello</template></custom>`,
214+
`<custom u-s="{{c}}" u-i="2a9ec0b0-0"><view wx:if="{{a}}" slot="header">hello</view><view wx:if="{{b}}" slot="footer">hello</view></custom>`,
215+
`(_ctx, _cache) => {
216+
return _e({ a: _ctx.a }, _ctx.a ? {} : {}, { b: _ctx.b }, _ctx.b ? {} : {}, { c: [_ctx.a ? 'header' : '', _ctx.b ? 'footer' : ''] })
217+
}`
218+
)
219+
220+
assert(
221+
`<custom><template v-if="a" #header>hello</template><template v-if="b">hello</template></custom>`,
222+
`<custom u-s="{{c}}" u-i="2a9ec0b0-0"><view wx:if="{{a}}" slot="header">hello</view><block wx:if="{{b}}">hello</block></custom>`,
223+
`(_ctx, _cache) => {
224+
return _e({ a: _ctx.a }, _ctx.a ? {} : {}, { b: _ctx.b }, _ctx.b ? {} : {}, { c: [_ctx.a ? 'header' : '', _ctx.b ? 'd' : ''] })
225+
}`
226+
)
227+
228+
assert(
229+
`<custom><template v-if="a" #header>hello</template><template v-else #footer>hello</template></custom>`,
230+
`<custom u-s="{{b}}" u-i="2a9ec0b0-0"><view wx:if="{{a}}" slot="header">hello</view><view wx:else slot="footer">hello</view></custom>`,
231+
`(_ctx, _cache) => {
232+
return _e({ a: _ctx.a }, _ctx.a ? {} : {}, { b: [_ctx.a ? 'header' : '', !_ctx.a ? 'footer' : ''] })
233+
}`
234+
)
235+
236+
assert(
237+
`<custom><template v-if="a" #header>hello</template><template v-else>hello</template></custom>`,
238+
`<custom u-s="{{b}}" u-i="2a9ec0b0-0"><view wx:if="{{a}}" slot="header">hello</view><block wx:else>hello</block></custom>`,
239+
`(_ctx, _cache) => {
240+
return _e({ a: _ctx.a }, _ctx.a ? {} : {}, { b: [_ctx.a ? 'header' : '', !_ctx.a ? 'd' : ''] })
241+
}`
242+
)
243+
244+
assert(
245+
`<custom><template v-if="a" #header>hello</template><template v-else-if="b" #footer>hello</template></custom>`,
246+
`<custom u-s="{{c}}" u-i="2a9ec0b0-0"><view wx:if="{{a}}" slot="header">hello</view><view wx:elif="{{b}}" slot="footer">hello</view></custom>`,
247+
`(_ctx, _cache) => {
248+
return _e({ a: _ctx.a }, _ctx.a ? {} : _ctx.b ? {} : {}, { b: _ctx.b, c: [_ctx.a ? 'header' : '', !_ctx.a && _ctx.b ? 'footer' : ''] })
249+
}`
250+
)
251+
252+
assert(
253+
`<custom><template v-if="a" #header>hello</template><template v-else-if="b" #footer>hello</template><template v-else-if="c" #header2>hello</template></custom>`,
254+
`<custom u-s="{{d}}" u-i="2a9ec0b0-0"><view wx:if="{{a}}" slot="header">hello</view><view wx:elif="{{b}}" slot="footer">hello</view><view wx:elif="{{c}}" slot="header2">hello</view></custom>`,
255+
`(_ctx, _cache) => {
256+
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' : ''] })
257+
}`
258+
)
259+
260+
assert(
261+
`<custom><template v-if="a" #header>hello</template><template v-else-if="b" #footer>hello</template><template v-if="c" #header2>hello</template></custom>`,
262+
`<custom u-s="{{d}}" u-i="2a9ec0b0-0"><view wx:if="{{a}}" slot="header">hello</view><view wx:elif="{{b}}" slot="footer">hello</view><view wx:if="{{c}}" slot="header2">hello</view></custom>`,
263+
`(_ctx, _cache) => {
264+
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' : ''] })
265+
}`
266+
)
267+
268+
assert(
269+
`<custom><template v-if="a" #header>hello</template><template v-else-if="b" #footer>hello</template><template v-else #footer2>hello</template></custom>`,
270+
`<custom u-s="{{c}}" u-i="2a9ec0b0-0"><view wx:if="{{a}}" slot="header">hello</view><view wx:elif="{{b}}" slot="footer">hello</view><view wx:else slot="footer2">hello</view></custom>`,
271+
`(_ctx, _cache) => {
272+
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' : ''] })
273+
}`
274+
)
275+
276+
assert(
277+
`<custom><template v-if="a" #header>hello</template><template v-else-if="b" #footer>hello</template><template v-else-if="c" #footer3>hello</template><template v-else #footer2>hello</template></custom>`,
278+
`<custom u-s="{{d}}" u-i="2a9ec0b0-0"><view wx:if="{{a}}" slot="header">hello</view><view wx:elif="{{b}}" slot="footer">hello</view><view wx:elif="{{c}}" slot="footer3">hello</view><view wx:else slot="footer2">hello</view></custom>`,
279+
`(_ctx, _cache) => {
280+
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' : ''] })
281+
}`
282+
)
283+
284+
assert(
285+
`<custom><template v-if="a" #[header]>hello</template></custom>`,
286+
`<custom u-s="{{c}}" u-i="2a9ec0b0-0"><view wx:if="{{a}}" slot="{{b}}">hello</view></custom>`,
287+
`(_ctx, _cache) => {
288+
return _e({ a: _ctx.a }, _ctx.a ? { b: _d(_ctx.header) } : {}, { c: _d([_ctx.a ? _ctx.header : ""]) })
289+
}`
290+
)
291+
292+
assert(
293+
`<custom><template v-if="a" #[header]>hello</template><template v-else>hello</template></custom>`,
294+
`<custom u-s="{{c}}" u-i="2a9ec0b0-0"><view wx:if="{{a}}" slot="{{b}}">hello</view><block wx:else>hello</block></custom>`,
295+
`(_ctx, _cache) => {
296+
return _e({ a: _ctx.a }, _ctx.a ? { b: _d(_ctx.header) } : {}, { c: _d([_ctx.a ? _ctx.header : "", !_ctx.a ? "d" : ""]) })
297+
}`
298+
)
299+
})

packages/uni-mp-compiler/src/transforms/vSlot.ts

Lines changed: 114 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
type ComponentNode,
1313
type CompoundExpressionNode,
1414
type DirectiveNode,
15+
type ElementNode,
1516
ElementTypes,
1617
ErrorCodes,
1718
type ExpressionNode,
@@ -50,6 +51,13 @@ import {
5051
} from './utils'
5152
import { createVForArrowFunctionExpression } from './vFor'
5253
import { DYNAMIC_SLOT } from '../runtimeHelpers'
54+
import { processExpression } from './transformExpression'
55+
56+
type Condition = {
57+
slotName: string | CompoundExpressionNode
58+
condition: string
59+
expression?: ExpressionNode
60+
}
5361

5462
export const transformSlot: NodeTransform = (node, context) => {
5563
if (!isUserComponent(node, context as any)) {
@@ -58,6 +66,7 @@ export const transformSlot: NodeTransform = (node, context) => {
5866

5967
const { tag, children } = node
6068
const slots = new Set<string | ExpressionNode>()
69+
const conditions: Condition[] = []
6170
const onComponentSlot = findDir(node, 'slot', true)
6271
const implicitDefaultChildren: TemplateChildNode[] = []
6372
const isMiniProgramComponent = context.isMiniProgramComponent(tag)
@@ -83,6 +92,12 @@ export const transformSlot: NodeTransform = (node, context) => {
8392
if (slotElement.type !== NodeTypes.COMMENT) {
8493
implicitDefaultChildren.push(slotElement)
8594
}
95+
if (
96+
slotElement.type === NodeTypes.ELEMENT &&
97+
slotElement.tag === 'template'
98+
) {
99+
buildConditions(slotElement, SLOT_DEFAULT_NAME, conditions)
100+
}
86101
continue
87102
}
88103

@@ -114,6 +129,9 @@ export const transformSlot: NodeTransform = (node, context) => {
114129

115130
if (slotName) {
116131
slots.add(slotName)
132+
if (slotElement.tag === 'template') {
133+
buildConditions(slotElement, slotName, conditions)
134+
}
117135
}
118136
}
119137

@@ -136,6 +154,7 @@ export const transformSlot: NodeTransform = (node, context) => {
136154
transformTemplateSlotElement(onComponentSlot, templateNode, node, context)
137155
node.children = [templateNode]
138156
}
157+
const slotConditionMap = buildSlotConditionMap(conditions)
139158
// 不支持 $slots, 则自动补充 props
140159
if (slots.size && !context.miniProgram.slot.$slots) {
141160
const slotsArr = [...slots]
@@ -145,10 +164,27 @@ export const transformSlot: NodeTransform = (node, context) => {
145164
const children: (string | ExpressionNode)[] = []
146165
const len = slotsArr.length - 1
147166
slotsArr.forEach((name, index) => {
148-
if (isString(name)) {
149-
children.push(`'${dynamicSlotName(name)}'`)
167+
if (slotConditionMap.has(name)) {
168+
const slotName = isString(name)
169+
? createSimpleExpression(dynamicSlotName(name), true)
170+
: name
171+
children.push(
172+
createCompoundExpression([
173+
processExpression(
174+
createSimpleExpression(
175+
genExpr(slotConditionMap.get(name) as ExpressionNode),
176+
false
177+
),
178+
context
179+
),
180+
' ? ',
181+
slotName,
182+
' : ',
183+
createSimpleExpression('', true),
184+
])
185+
)
150186
} else {
151-
children.push(name)
187+
children.push(isString(name) ? `'${dynamicSlotName(name)}'` : name)
152188
}
153189
if (index < len) {
154190
children.push(',')
@@ -161,7 +197,12 @@ export const transformSlot: NodeTransform = (node, context) => {
161197
])
162198
} else {
163199
value = `[${slotsArr
164-
.map((name) => `'${dynamicSlotName(name as string)}'`)
200+
.map((name) => {
201+
const slotName = dynamicSlotName(name as string)
202+
return slotConditionMap.has(name)
203+
? `${genExpr(slotConditionMap.get(name))} ? '${slotName}' : ''`
204+
: `'${slotName}'`
205+
})
165206
.join(',')}]`
166207
}
167208
node.props.unshift(createBindDirectiveNode(ATTR_VUE_SLOTS, value))
@@ -419,3 +460,72 @@ export function createVSlotCallExpression(
419460
]),
420461
])
421462
}
463+
464+
function buildConditions(
465+
node: ElementNode,
466+
slotName: string | CompoundExpressionNode,
467+
conditions: Condition[]
468+
) {
469+
const directives = [
470+
{ condition: 'if', hasExpr: true },
471+
{ condition: 'else-if', hasExpr: true },
472+
{ condition: 'else', hasExpr: false },
473+
]
474+
for (const { condition, hasExpr } of directives) {
475+
const dir = findDir(node, condition, true)
476+
if (dir && (!hasExpr || dir.exp)) {
477+
conditions.push({
478+
slotName,
479+
condition,
480+
expression: hasExpr ? dir.exp : undefined,
481+
})
482+
return
483+
}
484+
}
485+
}
486+
487+
function buildSlotConditionMap(conditions: Condition[]) {
488+
const slotConditionMap = new Map()
489+
const expressions: ExpressionNode[] = []
490+
for (const condition of conditions) {
491+
switch (condition.condition) {
492+
case 'if':
493+
// 直接设置为当前条件
494+
slotConditionMap.set(condition.slotName, condition.expression!)
495+
expressions.push(condition.expression!)
496+
break
497+
case 'else-if':
498+
// else-if 前面的条件都取反,并当前条件
499+
const expr = createCompoundExpression([
500+
...expressions
501+
.map((expr, idx) => [
502+
idx === 0 ? '' : ' && ',
503+
createCompoundExpression(['!', '(', expr, ')']),
504+
])
505+
.flat(),
506+
' && ',
507+
condition.expression!,
508+
])
509+
expressions.push(condition.expression!)
510+
slotConditionMap.set(condition.slotName, expr)
511+
break
512+
case 'else':
513+
// 条件都取反
514+
slotConditionMap.set(
515+
condition.slotName,
516+
createCompoundExpression(
517+
expressions
518+
.map((expr, idx) => [
519+
idx === 0 ? '' : ' && ',
520+
createCompoundExpression(['!', '(', expr, ')']),
521+
])
522+
.flat()
523+
)
524+
)
525+
// 清空存储的 if/else-if 表达式
526+
expressions.length = 0
527+
break
528+
}
529+
}
530+
return slotConditionMap
531+
}

packages/uni-mp-core/src/runtime/componentProps.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,17 @@ function initDefaultProps(
4848
const $slots = Object.create(null)
4949
newVal &&
5050
newVal.forEach((slotName: string) => {
51-
$slots[slotName] = true
51+
if (slotName) {
52+
$slots[slotName] = true
53+
}
5254
})
5355
this.setData({
5456
$slots,
5557
})
58+
if (this.$vm) {
59+
this.$vm.$.slots = $slots
60+
this.$vm.$forceUpdate()
61+
}
5662
}
5763
properties.uS = {
5864
type: null,

0 commit comments

Comments
 (0)