前言
要在 uni-app 中使用动态 slot 名字,会比较麻烦,因为:在 MP-WEIXIN、APP-PLUS 都会有坑。
H5 和 小程序端
我们先说比较常用的 H5 和 MP-WEIXIN 好了:
定义:
<!-- HACK: uni-app 处理动态 slot 名字不兼容,需要使用不同的语法 -->
<!-- #ifdef H5 -->
<slot :name="`tab:${item.key}`"></slot>
<!-- #endif -->
<!-- #ifdef MP-WEIXIN-->
<slot name="tab:{{item.key}}"></slot>
<!-- #endif -->
使用 slot:
<view>
<!-- HACK: uni-app 处理动态 slot 名字不兼容,需要使用不同的语法 -->
<!-- #ifdef H5 -->
<template v-for="item in list" :slot="`tab:${item.id}`">
<post-list :key="item.id" />
</template>
<!-- #endif -->
<!-- #ifdef MP-WEIXIN-->
<template v-for="item in lits" slot="tab:professional:{{item.id}}">
<post-list :key="item.id" />
</template>
<!-- #endif -->
</view>
参考链接:https://ask.dcloud.net.cn/question/82506
APP 端
如果还要兼容 APP 端(vue 文件),则情况会变得稍微复杂一点,以上两种情况都不适用,先说结论:
- 不支持拿 data 的数据用于拼接动态 slot 名字
- 在 v-for 中要根据当前项的字段来拼接 slot 名字,则要将 key 指向
item
本身 - 如果不想 key 指向 item,则只能拿 v-for 的 index 来拼接 slot 名字
解决方案
**
也即,如果是上面的例子,需要改写为如下::
<swiper-item v-for="(item, index) in tabs" :key="item.id" class="swiper-item">
<!-- HACK: uni-app 处理动态 slot 名字不兼容,需要使用不同的语法 -->
<!-- #ifdef H5 || APP-PLUS -->
<slot :name="`tab:${index}`"></slot>
<!-- #endif -->
<!-- #ifdef MP-WEIXIN-->
<slot name="tab:{{index}}"></slot>
<!-- #endif -->
</swiper-item>
使用的时候:
<tab-swiper
ref="tabSwiper"
:tabs="list"
:current.sync="current"
:swiper-current.sync="swiperCurrent"
>
<!-- HACK: uni-app 处理动态 slot 名字不兼容,需要使用不同的语法 -->
<!-- #ifndef H5 || APP-PLUS -->
<template v-for="(item, index) in list" :slot="`tab:${index}`">
<post-list :key="item.id" :stagger="index % 2 !== 0" />
</template>
<!-- #endif -->
<!-- #ifdef MP-WEIXIN-->
<template v-for="(item, index) in list" slot="tab:{{item.id}}">
<post-list :key="item.id" :stagger="index % 2 !== 0" />
</template>
<!-- #endif -->
</tab-swiper>
排查问题
下面开始排查问题,首先我们用以下代码测试用 data 的数据来作为 slot 名字:
<template>
<view>
testing dynamic slot
<slot :name="key"></slot>
<view :class="key">
test key value
</view>
</view>
</template>
<script>
export default {
data() {
return {
key: 'slot-1',
}
},
}
</script>
然后使用命令行或者 HBuilderx 编译 APP 端代码后,我们在 dist/dev/app-plus/app-view.js
搜索 testing dynamic slot
,然后可以看到以下代码:
可以看到,即便同样使用了 key 作为属性,但它们编译后的代码是不一样的,slot 节点直接使用 _vm._key
,而 view 节点变成了 _vm._$(2,'c')
,由此也推断出 uni-app 内部并没有对 slot 的 name
属性做额外处理,其实如果打印 _vm.key
的值, 会发现是空的:
所以结论一:不支持拿 data 的数据用于拼接动态 slot 名字。**
但直接拿 data 数据来拼接 slot 名字的情况比较少,更多时候是在 v-for 循环内部,所以我们再拿以下代码做测试:
<template>
<view>
testing dynamic slot
<view v-for="item in list" :key="item.id">
{{ item.name }}
<slot :name="`tab:${item.id}`"></slot>
</view>
</view>
</template>
<script>
export default {
data() {
return {
list: [
{
id: 'a',
name: 'item-a',
},
{
id: 'b',
name: 'item-b',
},
{
id: 'c',
name: 'item-c',
},
],
}
},
}
</script>
以上代码在 APP 端依然是不正常的,我们看看编译后的代码:
代码看上去好像挺正常的是吧,但有一点很奇怪:为什么 key 直接指向了 item
?
果不其然,打印 item
发现这里的 item
并不是 v-for 中的那个 item
对象 ,而是用于指定 key
的值:
所以 item.id
依然是空的,实际上这里的 item
=== 外部的 item.id
。
既然如此,我们似乎得到两个解决方案:
- 将 v-for 的 key 直接指向 item 本身,使
item.id
能正常访问 - 拼接 slot 名字时不使用
item.id
而是使用item
结论是,方案一可行;方案二不可行。
至于为什么方案二不可行,我认为是 uni-app 的问题,因为打印出来的值是正确的。
结论二:在 v-for 中要根据当前项的字段来拼接 slot 名字,则要将 key 指向 item
本身。
但将对象类型作为 VNode 的 key 似乎并不好,所以提出最后一个解决方案,那就是通过 index 拼接 slot 名字:
<template>
<view>
testing dynamic slot
<view v-for="(item, index) in list" :key="item.id">
{{ item.name }}
<slot :name="`tab:${index}`"></slot>
</view>
</view>
</template>
<script>
export default {
data() {
return {
list: [
{
id: 'a',
name: 'item-a',
},
{
id: 'b',
name: 'item-b',
},
{
id: 'c',
name: 'item-c',
},
],
}
},
}
</script>
以上代码能在 APP 端正常运行。
结论三:如果不想 key 指向 item,则只能拿 v-for 的 index 来拼接 slot 名字。
但毕竟是 HACK,终究原因是 uni-app 目前仍没有在 APP 端支持定义动态 slot ,可见相关的讨论:
- https://ask.dcloud.net.cn/question/95109
- 如何评价uni-app? - 蘑菇王的回答 - 知乎 https://www.zhihu.com/question/309490398/answer/1181409781
所以 uni-app 官方什么时候支持动态 slot 名字呢?
10 个评论
要回复文章请先登录或注册
2***@qq.com
1***@qq.com
DCloud_UNI_GSQ
DCloud_UNI_GSQ
c***@163.com
lindsy
lindsy
lindsy
一抱一个胖猪猪
1***@qq.com