g***@gmail.com
g***@gmail.com
  • 发布:2021-01-11 18:11
  • 更新:2022-08-22 20:24
  • 阅读:4117

动态插槽名问题讨论和 HACK 方案

分类:uni-app

前言

要在 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 文件),则情况会变得稍微复杂一点,以上两种情况都不适用,先说结论:

  1. 不支持拿 data 的数据用于拼接动态 slot 名字
  2. 在 v-for 中要根据当前项的字段来拼接 slot 名字,则要将 key 指向 item 本身
  3. 如果不想 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 ,然后可以看到以下代码:
image.png
可以看到,即便同样使用了 key 作为属性,但它们编译后的代码是不一样的,slot 节点直接使用 _vm._key ,而 view 节点变成了 _vm._$(2,'c') ,由此也推断出 uni-app 内部并没有对 slot 的 name 属性做额外处理,其实如果打印 _vm.key 的值, 会发现是空的:
image.png
**
所以结论一:不支持拿 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 端依然是不正常的,我们看看编译后的代码:
image.png
代码看上去好像挺正常的是吧,但有一点很奇怪:为什么 key 直接指向了 item

果不其然,打印 item 发现这里的 item 并不是 v-for 中的那个 item 对象 ,而是用于指定 key 的值:
image.png
所以 item.id 依然是空的,实际上这里的 item === 外部的 item.id

既然如此,我们似乎得到两个解决方案:

  1. 将 v-for 的 key 直接指向 item 本身,使 item.id 能正常访问
  2. 拼接 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 ,可见相关的讨论:

所以 uni-app 官方什么时候支持动态 slot 名字呢?

6 关注 分享
i***@126.com 一抱一个胖猪猪 lindsy 1***@qq.com pure111 x007xyz

要回复文章请先登录注册

1***@qq.com

1***@qq.com

回复 DCloud_UNI_GSQ :
manifest.json配置了这个还是不行
2022-08-22 20:24
DCloud_UNI_GSQ

DCloud_UNI_GSQ

小程序端也已支持(如仍然提示不支持动态插槽名,请设置 scopedSlotsCompiler 为 augmented)
2022-08-16 21:00
DCloud_UNI_GSQ

DCloud_UNI_GSQ

App 端已支持
2022-06-24 10:50
c***@163.com

c***@163.com

回复 lindsy :
楼主鬼才
2022-01-19 18:20
lindsy

lindsy

感谢楼主
2021-12-24 17:13
lindsy

lindsy

回复 lindsy :
刚刚测试了下,可以,虽然父组件slot的可读性不那么强,但是已经很感动了!。
2021-12-24 17:12
lindsy

lindsy

牛!
2021-12-24 16:57
一抱一个胖猪猪

一抱一个胖猪猪

虽然看了还没测试,但不缺为你点赞
2021-10-07 07:57
1***@qq.com

1***@qq.com

赞,刚好遇见你
2021-01-14 12:14