vue 通过封装列表卡片了解slot的传值方式

2,618 阅读4分钟

slot 平时不常用 今天封装一个卡片列表组件学习一下!

首先写出来一个卡片的样式

<div class="card p-3 mt-3 bg-white"> 
    <div class="card-header pb-3 d-flex ai-center"> 
      <i class="iconfont icon-menu" ></i>
      <div class="fs-xl flex-1 ml-2">卡片标题</div>
      <i class="iconfont icon-more1"></i>
    </div>
    
    <div class="card-body pt-3"> 
        <div class="nav jc-between">
            <div  class="nav-item active">
                <div class="nav-link">分类标题1</div>
            < /div>
            <div  class="nav-item">
                <div class="nav-link">分类标题2</div>
            < /div>
            <div  class="nav-item">
                <div class="nav-link">分类标题3</div>
            < /div>
        </div>
    </div>
    
    <div class="pt-3">
      <swiper>
      <!--循环出来3个分类对应的3块slide-->
        <swiper-slide v-for="(item,index) in 3 " :key="index">
            <!--循环每一个slide的每一条数据-->
            <div v-for="(itm,idx) in item.Newslist" :key="idx" class="py-2">
              <span>{{itm.name}}</span>
              <span>{{itm.title}}</span>
              <span>{{itm.date}}</span>
            </div>
        </swiper-slide>
      </swiper>
    </div>
    </div>
  </div>

这样我们就实现了一个卡片列表 我们把上面的封装为组件

// Card.vue
<template>
    <div class="card p-3 mt-3 bg-white"> 
      <div class="card-header pb-3 d-flex ai-center"> 
        <i class="iconfont" :class="`icon-${icon}`"></i>
        <!--卡片标题-->
        <div class="fs-xl flex-1 ml-2">{{title}}</div>
        <i class="iconfont icon-more1"></i>
      </div>
      <div class="card-body pt-3">
        <!-- 预留插槽  外部引用组件时可以传递内容 因为每一个卡片内部的排版不一致 -->
        <slot></slot>
      </div>
    </div>
</template>


props: {
  title: {
    type: String, 
    required: true
  },
  icon: {
    type: String, 
    required: true
  }
}

// main.js注册为全局组件
// 卡片组件
  import Card from './components/Card.vue';
  Vue.component('m-card', Card);

home.vue 使用组件

  // home.vue 使用组件 
    <m-card :icon="icon" :title="title">
        <!-- 将下面放入card-body的插槽中 -->
        <div class="nav jc-between">
          <div class="nav-item active">
                <div class="nav-link">分类标题1</div>
          </div>
          <div  class="nav-item">
                <div class="nav-link">分类标题2</div>
          </div>
          <div  class="nav-item">
                <div class="nav-link">分类标题3</div>
          </div>
        </div>
        <div class="pt-3">
          <swiper>
          <!--循环出来3个分类对应的3块slide-->
            <swiper-slide v-for="(item,index) in 3 " :key="index">
              <div v-for="(itm,idx) in item.Newslist" :key="idx" class="py-2">
                  <span>{{itm.name}}</span>
                  <span>{{itm.title}}</span>
                  <span>{{itm.date}}</span>
              </div>
            </swiper-slide>
          </swiper>
        </div>
  </m-card>

这样我们使用card组件传递标题名和字体图标类名即可 但是还不能满足我们深度封装的需求 所以我们继续改造

// ListCard.vue
<template>
    <m-card :icon="icon" :title="title">
      <!-- 将下面放入card-body的插槽中 -->
      <div class="nav jc-between">
        <div class="nav-item active">
              <div class="nav-link">分类标题1</div>
        </div>
        <div  class="nav-item">
              <div class="nav-link">分类标题2</div>
        </div>
        <div  class="nav-item">
              <div class="nav-link">分类标题3</div>
        </div>
      </div>
      <div class="pt-3">
        <swiper>
          <swiper-slide v-for="(item,index) in 3 " :key="index">
            <div v-for="(itm,idx) in item.Newslist" :key="idx" class="py-2">
                <span>{{itm.name}}</span>
                <span>{{itm.title}}</span>
                <span>{{itm.date}}</span>
            </div>
          </swiper-slide>
        </swiper>
      </div>
    </m-card>
</template>

props: {
  title: {
    type: String,
    required: true
  },
  icon: {
    type: String,
    required: true
  },
  categories: {
    type: Array,
    required: true
  }
}

// main.js 封装列表卡片
// 列表卡片组件
  import ListCard from './components/ListCard.vue';
  Vue.component('m-list-card', ListCard);

这样我们在引用组件时这样使用

// home.js
<m-list-card icon="menu" title="新闻资讯" :categories="NewsData"> 
</m-list-card>

但是这样又带了一个问题 卡片列表部分的样式不可修改 我们可以利用插槽的 #items 来解决

// ListCard.vue 将swiper-slide 展示列表的部分这样改
   <swiper>
       <swiper-slide v-for="(item,index) in categories " :key="index">
         <!-- <div v-for="(itm,idx) in item.Newslist" :key="idx" class="py-2">
             <span>{{itm.name}}</span>
             <span>{{itm.title}}</span>
             <span>{{itm.date}}</span>
         </div>-->
         <!-- 由于每个卡片的样式不同 所以这个地方用slot 并且把循环的值传给外层 -->
         <slot name="items" :category="item"></slot>
       </swiper-slide>
    </swiper>
    
// home.vue
  <m-list-card icon="menu" title="新闻资讯" :categories="NewsData">
     <!-- #表示要与某一个slot关联 并从中取值-->
     <template #items="{category}">
       <div v-for="(itm,idx) in category.Newslist" :key="idx" class="py-2">
         <span>{{itm.name}}</span>
         <span>{{itm.title}}</span>
         <span>{{itm.date}}</span>
       </div>
     </template>
   </m-list-card>
这个组件的主要难点在于 组件内的循环的数据如何传递给外部 我们定义一个插槽时 可以为其指定name 并且绑定动态数据
当需要接收插槽的数据时 通过#items="{插槽绑定的变量名}" 来接收