vue插槽详解

声明:在 2.6.0 中,我们为具名插槽和作用域插槽引入了一个新的统一的语法 (即 'v-slot' 指令)。它取代了 'slot' 和 'slot-scope'这两个目前已被废弃但未被移除且仍在文档中的attribute,本文我们仅仅针对vue2.6及以上版本的插槽语法进行讲解!

一、什么是插槽?

1、定义

官方解释:插槽就是Vue实现的一套内容分发的API,将<slot></slot>元素作为承载分发内容的出口。
通俗解释:想要在一个组件标签中,加入一些内容,那就必须要在组件内声明slot元素,否则不会被渲染出来。

2、举个栗子

有这样一个自定义组件navigation-link,在父组件中使用:

<navigation-link url="/profile">
  Your Profile
</navigation-link>

navigation-link组件封装如下:

<a v-bind:href="url" class="nav-link">
  <slot></slot>
</a>

当组件渲染的时候,<slot></slot> 将会被替换为“Your Profile”。假设navigation-link组件中不写<slot></slot>,那么组件标签中间加入的任何内容都不会生效。

tips:插槽内可以包含任何模板代码,包括 HTML:例子如下

<navigation-link url="/profile">
  <!-- 添加一个 Font Awesome 图标 -->
  <span class="fa fa-user"></span> Your Profile
</navigation-link>
<navigation-link url="/profile">
   <!-- 添加一个图标的组件 -->
   <font-awesome-icon name="user"></font-awesome-icon> Your Profile
</navigation-link>

二、具名插槽

1、定义

具名插槽,就是给这个插槽起个名字。一个子组件可以放多个插槽,而且可以放在不同的地方,而父组件填充内容时,可以根据这个名字把内容填充到对应插槽中。

2、如何使用

<slot> 元素有一个特殊的 attribute:name。这个name属性的值就是插槽的名字,父组件中在一个 <template> 元素上使用v-slot 指令,并以 v-slot 的参数的形式提供其名称,一一对应。

2、举个栗子

有这样一个组件<base-layout>,给它创建三个插槽

<div class="container">
   <header>
       <slot name="header"></slot>
   </header>
   <main>
       <!-- 不带 name 的 <slot> 出口会带有隐含的名字“default”,即默认插槽-->
       <slot></slot> 
   </main>
   <footer>
       <slot name="footer"></slot>
   </footer>
</div>

父组件中代码如下:

<base-layout>
  <template v-slot:header>
    <h1>Here might be a page title</h1>
  </template>
   <!-- 没有被包裹在带有 v-slot 的 <template> 中的内容都会被视为默认插槽的内容-->
  <p>A paragraph for the main content.</p>
  <p>And another one.</p>

  <template v-slot:footer>
    <p>Here's some contact info</p>
  </template>
</base-layout>

三、作用域插槽

1、背景

看个栗子: 还记得前面提到的navigation-link组件吗?假设我们想在这个父模板中使用传递给子组件的数据url,是会拿不到的,这里url会是undefined

<navigation-link url="/profile">
  点击这里跳转: {{ url }}
  <!--
  这里的 `url` 会是 undefined,因为其 (指该插槽的) 内容是
  _传递给_ <navigation-link> 的而不是
  在 <navigation-link> 组件*内部*定义的。
  -->
</navigation-link>

谨记一条规则:
父级模板里的所有内容都是在父级作用域中编译的;
子模板里的所有内容都是在子作用域中编译的。
所以想在父作用域中访问子组件中的数据,那我们的作用域插槽就派上了用场啦~

2、概念

作用域插槽其实就是带数据的插槽,简单点说就是子组件提供给父组件的数据,父组件可根据子组件传过来的插槽数据来进行不同的方式展现和填充插槽内容。

3、举个栗子

有这样一个导航组件nav-link

<ul>
     // navLists是父组件传递过来的数据,放在props中(props:['navLists'])
     <li v-for="list in navLists"> 
        <a :href="list.url" class="nav-link">
            <slot :rowData="list">{{list.name}}</slot>
        </a>
    </li>
</ul>

父组件中代码

//数据如下:
<!--
lists: [
    {name:'首页', url:'/home'},
    {name:'关于', url:'/about'},
    {name:'新闻', url:'/news'}
  ]
-->
<nav-link :navLists="lists">
    <template v-slot:default="slotProps">
        {{slotProps}}
    </template>
</nav-link>

页面显示如下:


假设现在我们想展示的更个性化一点,让首页导航前面显示一个小图标,那应该怎么做?

<nav-link :navLists="lists">
    <template v-slot:default="slotProps">
      <span v-if="slotProps.rowData.name==='首页'">
          <font-awesome-icon name="home" />{{slotProps.rowData.name}}
       </span>
      <span v-else>{{slotProps.rowData.name}}</span>
    </template>
</nav-link>

tips1: 默认插槽的缩写形式
上面例子中因为子组件slot元素未设置name属性,所以就是默认插槽,因此’v-slot:default="slotProps"可以缩写为v-slot="slotProps",但是缩写语法不能和具名插槽混用,否则会导致作用域不明确。如果出现多个插槽时,请始终为所有的插槽使用完整的基于 <template> 的语法:

<nav-link :navLists="lists">
  <template v-slot:default="slotProps">
    {{ slotProps.rowData.name }}
  </template>

  <template v-slot:other="otherSlotProps">
    ...
  </template>
</nav-link>

tips2: 解构插槽Prop
作用域插槽的内部工作原理是将你的插槽内容包括在一个传入单个参数的函数里,因此v-slot的值实际上可以是任何能够作为函数定义中的参数的 JavaScript 表达式

<nav-link :navLists="lists">
    <template v-slot="{ rowData }">
      <span v-if="rowData.name==='首页'">
          <font-awesome-icon name="home" />{{.rowData.name}}
       </span>
      <span v-else>{{rowData.name}}</span>
    </template>
</nav-link>

哈哈,上面这种写法,用过element-ui的童鞋们,是不是觉得超级熟悉,在表格中,我们要自定义某列数据的时候,经常会用到slot-scope(也就是文中提到的v-slot,因为下图中的项目代码还是之前用的vue2.5,而v-slot是我们vue2.6以上新出来的语法), 其实表格的本质就是这样!


四、总结

v-slot用法简记:
:后面是插槽名称
=后面是组件内部绑定作用域值得映射

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。