1. 初步了解插槽

什么是插槽?

为什么要使用插槽?
在组件通信中(详情见文章:Vue组件学习、组件通信),我们了解到组件之间如何传递数据。那父组件在复用子组件的时候除了想给子组件传递数据,还想更改子组件的内容和样式时,怎么办呢?
一般来说,父组件是没办法更改子组件的内容和样式的,总结就是“你可以用我但是不可以更改我”。
就像买车一样 不能自己去决定车的配置、外观、性能、尺寸,这些都是厂家自己设计好然后批量生产的 ,大家买下都是一样的。

插槽的作用:
让父组件向子组件指定位置插入html结构,也是一种组件间的通信方式。

2. 插槽的分类

2.1 默认插槽

案例场景:以下是一个组件的复用(一个蓝色的div即是一个组件),该组件在页面中复用了3次,我想对某一个组件实现私人定制,比如,美食组件我只想放一张图片,电影组件中我想放一个视频,如何做?
Vue学习---插槽篇-LMLPHP
由上方效果变成下面这样的:
Vue学习---插槽篇-LMLPHP
制定子组件Category.vue:
这是没有使用插槽的情况,组件不能实现定制化:

<template>
  <div class="category">
    <h3>{{title}}</h3>    
    <ul>
      <li v-for="(item,index) in listData" :key="index">{{item}}</li>
    </ul>
  </div>
</template>
<script>
  export default{
    name:"Category",
    // 接收父组件传过来的值
    props:['listData','title']
  }
</script>
<style>
  .category{
    background-color: skyblue;
    width: 200px;
    height: 300px;
  }
  h3{
    text-align: center;
    background-color: orange;
  }
</style>

在App.vue中使用Category.vue:

<template>
  <div id="app">
    <!-- 复用子组件 静态传输标题,动态传输数组-->
   <Category title="美食" :listData="foods" />
   <Category title="书籍" :listData="books"/>
   <Category title="电影" :listData="films"/>
  </div>
</template>

<script scope="this api replaced by slot-scope in 2.5.0+">
import Category from './components/Category.vue';
export default {
  name: 'App',
  components: {
    Category,
  },
  data(){
    return{
      // 定义数据
      foods:['火锅','重庆火锅','小龙虾','牛排'],
      books:['语文','数学','英语','化学'],
      films:['重庆森林','七龙珠','犬夜叉','心灵捕手'],
    }
  }
}
</script>

<style>
#app {
  display: flex;
  justify-content: space-around;
}
</style>

以上复用只能出现第一个页面效果,见下图:
Vue学习---插槽篇-LMLPHP
如果不使用作用域插槽,仅在App.vue更改组件中内容,子组件是不会将内容渲染的
在App.vue中更改子组件内容,此时子组件中无插槽!!!

   <Category title="美食" >
    <img src="@/assets/hotpot.jpg" alt="">
  </Category>
   <Category title="书籍" :listData="books"/>
   <Category title="电影" :listData="films"/>

子组件无法渲染在父组件中更改的页面结构,如下图:
Vue学习---插槽篇-LMLPHP
下面使用默认插槽:
在子组件Category.vue中定义一个默认插槽:

<template>
  <div class="category">
    <h3>{{title}}</h3>
    <!-- 定义一个插槽 组件的页面结构完全交给父组件去定义-->
    <slot>默认值,当父组件没有出现时,我会出现</slot>
  </div>
</template>
<script>
  export default{
    name:"Category",
    // 接收父组件传过来的值
    props:['title']
  }
</script>
<style>
  .category{
    background-color: skyblue;
    width: 200px;
    height: 300px;
  }
  h3{
    text-align: center;
    background-color: orange;
  }
</style>

App.vue:

<template>
  <div id="app">
    <!-- 复用子组件 静态传输标题,动态传输数据 -->
   <Category title="美食" >
    <img src="@/assets/hotpot.jpg" alt="">
  </Category>

   <!-- <Category title="书籍" :listData="books"/> -->
   <Category title="书籍">
    <ul>
      <li v-for="(item,index) in books" :key="index">{{item}}</li>
    </ul>
  </Category>

   <!-- <Category title="电影" :listData="films"/> -->
   <Category title="电影">
    <video src="@/assets/1.mp4" controls></video>
  </Category>
  </div>
</template>

<script scope="this api replaced by slot-scope in 2.5.0+">
import Category from './components/Category.vue';
export default {
  name: 'App',
  components: {
    Category,
  },
  data(){
    return{
      // 定义数据
      foods:['火锅','重庆火锅','小龙虾','牛排'],
      books:['语文','数学','英语','化学'],
      films:['重庆森林','七龙珠','犬夜叉','心灵捕手'],
    }
  }
}
</script>

<style>
#app {
  display: flex;
  justify-content: space-around;
}
img{
  width: 100%;
}
video{
  width: 100%;
}
</style>

注意:对样式的定义,可以定义在父组件也可以定义在子组件。
区别:

  • 定义在父组件,结构先编译好样式再传给子组件;
  • 定义在子组件中,结构传给子组件,再由子组件编译。
<style>
img{
  width: 100%;
}
video{
  width: 100%;
}
</style>

2.2 具名插槽

上诉案例中,提高需求,我想在分割线下添加一个跳转链接(分割线以上的都是默认插槽区域),如何做?
Vue学习---插槽篇-LMLPHP
使用默认插槽和使用作用域插槽的区别:
Vue学习---插槽篇-LMLPHP

Category.vue

<template>
  <div class="category">
    <h3>{{title}}</h3>
    <!-- 定义一个插槽 -->
    <slot>默认值,当父组件没有出现时,我会出现</slot>
    -------------------------
    <!-- 声明一个具名插槽 -->
    <slot name="link"></slot>
    <!-- <ul>
      <li v-for="(item,index) in listData" :key="index">{{item}}</li>
    </ul> -->
  </div>
</template>

App.vue

<template>
  <div id="app">
    <!-- 复用子组件 静态传输标题,动态传输数据 -->
    <Category title="美食" >
    <img src="@/assets/hotpot.jpg" alt="">
     <!-- 将元素插入指定名字插槽内 -->
    <!-- vue对具名插槽的新写法 简写形式:#插槽名 -->
    <template >
      <div class="foot"><a href="">更多美食</a></div>
    </template>
  </Category>

   <!-- <Category title="书籍" :listData="books"/> -->
   <Category title="书籍">
    <ul>
      <li v-for="(item,index) in books" :key="index">{{item}}</li>
    </ul>
    <!-- 将元素插入指定名字插槽内 -->
    <!-- 传统用法:slot="插槽名" -->
    <div class="foot" slot="link" >
      <a href="https://www.taobao.com/" >去淘宝</a>
      <a href="https://www.taobao.com/" >去京东</a>
    </div>
    
    <br>
    <!--<a href="https://www.taobao.com/">去淘宝</a>
    <br>
    <a href="https://www.taobao.com/">去淘宝</a> -->
  </Category>

   <!-- <Category title="电影" :listData="films"/> -->
   <Category title="电影">
    <video src="@/assets/1.mp4" controls></video>
    <!-- 将元素插入指定名字插槽内-->
    <!-- vue对具名插槽的新写法,v-slot:插槽名-->
    <template v-slot:link>
      <div class="foot">
        <a href="">经典</a>
        <a href="">热门</a>
        <a href="">推荐</a>
      </div>
      <h4>欢迎前来观影</h4>
    </template>
  </Category>
  </div>
</template>

<script scope="this api replaced by slot-scope in 2.5.0+">
import Category from './components/Category.vue';
export default {
  name: 'App',
  components: {
    Category,
  },
  data(){
    return{
      // 定义数据
      foods:['火锅','重庆火锅','小龙虾','牛排'],
      books:['语文','数学','英语','化学'],
      films:['重庆森林','七龙珠','犬夜叉','心灵捕手'],
    }
  }
}
</script>

<style >
#app,.foot{
  display: flex;
  justify-content: space-around;
}
img{
  width: 100%;
}
video{
  width: 100%;
}
h4{
  text-align: center;
}
</style>

2.3 作用域插槽

理解: 数据在组件的自身,但根据数据生成的结构需要组件的使用者来决定。
案例:
Vue学习---插槽篇-LMLPHP
父组件:

<Category title="游戏">
<!-- 一定要使用template模板编译 -->
      <template scope="row">
        <!-- {{row.games}} -->
        <ul>
          <li v-for="(g, index) in row.games" :key="index">{{ g }}</li>
        </ul>
      </template>
    </Category>
    <Category title="游戏">
    <!-- 一定要使用template模板编译 -->
      <template scope="row">
        <ol>
          <li style="color: red" v-for="(g, index) in row.games" :key="index">
            {{ g }}
          </li>
        </ol>
      </template>
    </Category>
    <Category title="游戏">
    <!-- 一定要使用template模板编译 -->
      <template scope="row">
        <h4 v-for="(g, index) in row.games" :key="index">{{ g }}</h4>
      </template>
    </Category>

子组件中:

   <h3>{{title}}分类</h3>
   <!-- 定义一个作用域插槽 -->
   <slot :games='games'>默认内容</slot>
   <script>
    export default {
      props: ['title'],
      data() {
        return {
          games: ['红色警戒', '穿越火线', '劲舞团', '超级玛丽']
        }
      }
    }
</script>

作用域插槽可以看成数据的方向流动,有子组件传给父组件
注意:作用域插槽的使用一定要使用template模板

10-11 23:15