一篇文章搞定Vue中的插槽

210 阅读1分钟

Vue中的插槽

一.引出插槽

LT)9WA{[V6(R25)(23_`S.png

当我们有这种需求时,我们肯定会想到封装一个组件。

// 父组件中
<template>
  <div id="app">
  <Category title="美食" :dataList="foods"></Category>
  <Category title="电影" :dataList="films"></Category>
  <Category title="歌曲" :dataList="music"></Category>
 </div>
</template>
<script>
import Category from "./components/Category.vue"
export default {
 name: 'App',
 components: {
  Category
 },
 data() {
  return {
   // classification: ['美食', '游戏', '电影'],
   foods: ["油焖大虾", "鱼香肉丝", "糖醋排骨", "辣椒炒肉"],
   films: ["盗梦空间", "让子弹飞", "复仇者联盟", "不能说的秘密"],
   music: ["以父之名", "七里香", "夜的第七章", "乌克丽丽"]
  }
 }
}
</script>
​
// 子组件中
<template>
  <div class="wrap">
    <h3 class="title">{{ title }}分类</h3>
    <ul>
      <li v-for="item in dataList" :key="item">{{ item }}</li>
    </ul>
  </div>
</template>
<script>
export default {
  name: 'Category',
  props: ['dataList', 'title'],
};

这时,我们用props接收父组件传过来的数据,没有用到插槽就完美完成了需求~

二.插槽介绍

1.默认插槽

但是,需求一般不会这么简单的,每个列表中可能会有个性化的需求,像这样~

当然我们可以通过条件渲染v-for或者v-show实现,但是当这种列表有几百个呢?显然我们不可能写几百个的v-show嘛。

V}``Z~$6CDUP4S1I69X(Y84.png

此时,我们肯定不能在组件中加个标签嘛,那样做会导致三个列表中都会有图片。我们想到需要在父组件中定义结构,我们想到了下面的写法。

// 父组件中
<template>
  <div id="app">
  <Category title="美食">
    <div>
      <ul>
        <li v-for="item in foods" :key="item">{{ item }}</li>
      </ul>
      <img src="./static/vue.png" alt="">
    </div>
  </Category>
  <Category title="电影">
    <ul>
      <li v-for="item in films" :key="item">{{ item }}</li>
    </ul>
  </Category>
  <Category title="歌曲">
    <div>
      <ul>
        <li v-for="item in music" :key="item">{{ item }}</li>
      </ul>
      <img src="./static/qq.png" alt="">
    </div>
  </Category>
 </div>
</template>

我们在组件标签体中加入了标签,但是当我们打开浏览器时发现,标签并没有奏效。这是因为我们写在父组件标签体内的内容是需要插进子组件中,子组件解析完成后才能显示出来的,而Vue并不知道咱们加的标签应该插在组件的哪个部分,所以显示不出来。

Vue给我们提供了一个特殊的标签,用来占据位置。当父组件中有标签体内容时,这个标签被替换为我们写的标签。(挖个坑,等待组件的使用者填充)

// 子组件中
<template>
  <div class="wrap">
  <h3 class="title">{{title}}分类</h3>
  <slot></slot>
 </div>
</template>
注意:

标签内允许有内容,它将作为插槽的默认值,使用者不传值给他时,展示默认内容。

2.具名插槽

特殊需求又来了~~~

有些时候,我们在子组件中可能会使用到不止一个标签,用来让父组件不同的内容放进不同的插槽中。

当我们又在子组件中加了一个后,发现并不是我们想的让指定内容加到指定插槽,而是两个插槽中都是一样的内容。

NAZ3ZXB%{B%M@YBRUZMH3}L.png

这是因为Vue不知道我们要将哪些内容插到哪些插槽中,我们需要在内容与插槽之间建立一种联系,这时就用到了Vue中的具名插槽。

对于这样的情况,<slot> 元素有一个特殊的 attribute:name。这个 attribute 可以用来定义额外的插槽:

// 父组件中
<template>
  <div id="app">
  
  <Category title="美食">
   <template v-slot:header>
      <ul>
        <li v-for="item in foods" :key="item">{{ item }}</li>
      </ul>
      <img src="./static/vue.png" alt="" />
   </template>
   <template v-slot:footer>
      <a href="www.baidu.com">百度</a>
   </template>
  </Category>
  
  <Category title="电影">
      <ul shot="header">
        <li v-for="item in films" :key="item">{{ item }}</li>
      </ul>
  </Category>
  
  <Category title="歌曲">
    <template v-slot:header>
      <ul>
        <li v-for="item in music" :key="item">{{ item }}</li>
      </ul>
      <img src="./static/vue.png" alt="" />
    </template>
    <div slot="footer">
      <a href="www.jd.com">京东</a>
      <a href="www.taobao.com">淘宝</a>
   </div>
  </Category>
  
 </div>
</template>
// 子组件中
<template>
  <div class="wrap">
    <h3 class="title">{{title}}分类</h3>
    <slot name="header"></slot>
    <slot name="footer"></slot>
  </div>
</template>

%QB{5T8TB@_F`BAR9B8220J.png

注意:

这里使用了两种给父组件定义插槽名字的方法。

(1)v-slot:footer

只能在组件或template上使用,这是新的

(2)slot="footer"

可在任意标签上使用,即将废弃

3.作用域插槽

特殊需求又又来了~~

上面的情况是,data数据、html标签结构都存放在父组件中,我们可以轻松的拿到数据并把它渲染在页面上。当data数据存放在子组件中时,这时存在一个作用域问题,父组件无法拿到数据,我们怎样才能拿到数据呢?

这时我们就用到了Vue中的作用域插槽

为了让 films 在父级的插槽内容中可用,我们可以将 films作为 <slot> 元素的一个 attribute 绑定上去;

绑定在 <slot> 元素上的 attribute 被称为插槽 prop。现在在父级作用域中,我们可以使用带值的 v-slot或者 scope 来定义我们提供的插槽 prop 的名字:

// 父组件中
<template>
  <div id="app">
    <Category title="电影">
      <template scope="comData">
        <ul>
          <li v-for="item in comData.films" :key="item">{{ item }}</li>
        </ul>
      </template>
    </Category>
    <Category title="电影">
      <template slot-scope="comData">
        <ul>
          <li v-for="item in comData.films" :key="item">{{ item }}</li>
        </ul>
      </template>
    </Category>
    <Category title="电影">
      <template v-slot:default="comData">
        <ul>
          <li v-for="item in comData.films" :key="item">{{ item }}</li>
        </ul>
      </template>
    </Category>
    <Category title="电影">
      <template v-slot:default={films}>
        <ul>
          <li v-for="item in films" :key="item">{{ item }}</li>
        </ul>
      </template>
    </Category>
  </div>
</template><script>
import Category from "./components/Category.vue";
export default {
  name: "App",
  components: {
    Category,
  },
  data() {
    return {};
  },
};
</script>
​
// 子组件中
<template>
  <div class="wrap">
    <h3 class="title">{{title}}分类</h3>
    <slot :films="films"></slot>
  </div>
</template><script>
export default {
  name: 'Category',
  props: ['title'],
  data() {
    return {
      films: ["盗梦空间", "让子弹飞", "复仇者联盟", "不能说的秘密"]
    }
  }
};
</script>

此时我们拿到了传过来的数据,我们可以先看一下他是什么。

{ "films": [ "盗梦空间", "让子弹飞", "复仇者联盟", "不能说的秘密" ] }

是一个对象,里面包含了films字段,所以我们可以用comData.films拿到电影数据。

同时,我们可以用ES6中解构赋值拿到数据。scope="{films}"

注意:

(1)父组件想要拿到数据,必须用template包裹内容,给template加一个属性scope、slot-scope或者v-slot:default。

scope、slot-scope都为即将废弃的写法。

(2)父组件接受的数据可以起任意名,并不是子组件中叫films,父组件就一定要叫films。

三.拓展

1.默认插槽名字

默认插槽也有名字,叫default

2.v-slot位置问题

之前提过v-slot可以在组件或template上使用,什么情况可以在组件使用呢?

独占默认插槽

<Category title="电影" v-slot:default="{films}">
   <ul>
      <li v-for="item in films" :key="item">{{ item }}</li>
    </ul>
</Category>

3.具名插槽缩写

v-slot:header =======> #header

它必须有参数,即#后必须给他个名字

4.动态插槽名

<Category title="电影">
  <template v-slot:[activeName]>
    
  </template>
</Category>