vue中插槽slot问题:匿名,具名,作用域(传参)、解构等

7,951 阅读4分钟

1.插槽是什么(举个例子)

(1)匿名插槽

    // 父组件 father.vue

     <template>
        <div>
          <div>我是父组件</div>
          <sonslot>
            <p>能否显示</p>
          </sonslot>
        </div>
    </template>

    <script>
      import sonslot from './sonslot';
      export default {
        components: {
          sonslot
        }
      }
    </script>

    <style>
    // 子组件 sonslot.vue

     <template>
        <div>
             <p>我是子组件</p>
        </div>
    </template>

    <script>
      export default {
      }
    </script>

    <style>
       页面结果:
           我是父组件
           我是子组件

那我们想要把父组件中p标签得内容展示出来怎么办?(只需一步,修改下子组件)

    // 子组件 sonslot.vue

     <template>
        <div>
            <p>我是子组件</p>
            <slot></slot>
        </div>
    </template>

    <script>
      export default {
      }
    </script>

    <style>

页面结果:
我是父组件
我是子组件
是否显示

(2)默认插槽

有时我们父组件不在子组件里面写内容,这时候子组件的slot需要准备一个后备内容,也就是默认显示内容。如果父组件不使用插槽(调用子组件不写内容),那么我们就默认显示一个内容

    // 父组件 father.vue

     <template>
        <div>
          <div>我是父组件</div>
          <sonslot>
          </sonslot>
        </div>
    </template>

    <script>
      import sonslot from './sonslot';
      export default {
        components: {
          sonslot
        }
      }
    </script>

    <style>
    // 子组件 sonslot.vue

     <template>
        <div>
            <p>我是子组件</p>
            <slot>我是默认内容</slot>
        </div>
    </template>

    <script>
      export default {
      }
    </script>

    <style>
    页面结果:
            我是父组件
            我是子组件
            我是默认内容

由此可以看出当我们调用子组件时,没有写内容,插槽就会显示默认的内容,当我们父组件写内容时,就会显示父组件传过来的内容,不会显示我是默认内容了

插槽优点可以看出:可以实现组件的复用性,就是子组件slot来占个位置,不管外面传什么内容这里照常接收显示,在父组件调用子组件的时候写着组件内部的,会在子组件插槽内显示

2.具名插槽

(1)引用原因,由于匿名插槽的局限性

当我们想使用多个插槽时,也就是我想让我父组件传过来的内容显示在页面的不同位置,如果子组件有多个slot且没有名字就会按照父组件传来的顺序排序,不会按我们想要的位置排序,这就是需要具名插槽的原因,看不懂没关系,看下下面的例子就会明白了。

子组件:

<div class="container">
  <header>
    <!-- 我们希望把页头放这里 -->
  </header>
  <main>
    <!-- 我们希望把主要内容放这里 -->
  </main>
  <footer>
    <!-- 我们希望把页脚放这里 -->
  </footer>
</div>

有时候我们需要父组件传过来的内容想要按这种格式展示,所以就需要给子组件插槽起个名字,然后父组件调用子组件然后调用插槽的名字,然后子组件根据名字就会把对应的内容显示到对应的位置

(2)具名插槽例子

//父组件father
<sonslot>
  <template v-slot:footer>
    <p>我是footer的内容</p>
  </template>
  
  <template> //也可以写成v-slot="default"
      <p>我是默认的内容,slot不起名字时默认时defauLt</p>
  </template>
  
  <template v-slot:header>
    <h1>我是header的内容</h1>
  </template>
  
</sonslot>
//子组件sonslot
<template>
  <div class="container">
    <header>
      <slot name="header"></slot>
    </header>
    <main>
      <slot></slot>
    </main>
    <footer>
      <slot name="footer"></slot>
    </footer>
  </div>
</template>
页面结果:
        我是header的内容
        我是默认的内容,slot不起名字时默认时defauLt
        我是footer的内容

总结:我们slot起了名字,然后通过v-slot来调用这个名字,插槽就会对应匹配上,顺序时按照子组件插槽的顺序来排的

3.作用域插槽

(1)作用域例子

    //父组件father
    <template>
        <sonslot>
            <template>
                {{user.firstName}}
            </template>
        </sonslot>
    </template>

    //子组件sonslot
    <template>
        <span>
              <slot>{{ user.lastName }}</slot>
        </span>
    </template>
    export default {
        data() {
            return {
                user: {
                    firstName: 'Jay',
                    lastName: 'wang'
                }
            }
        }
    }
    
页面结果:可能报错了,因为作用域的问题,父组件获取不到子组件的user,父级元素是在父级渲染的,子级元素是子啊子集渲染的,互补干扰

那我们父组件想要获取到子组件的数据该如何做呢?

解决作用域问题

   //父组件father
   <template>
       <sonslot>
           <template v-slot:header="scope">
               {{scope.user.firstName }}
           </template>
       </sonslot>
   </template>
   //子组件sonslot
   <template>
       <slot name="header" :user="user">
           {{user.lastName }}
       </slot>
   </template>
   export default {
        data() {
            return {
                user: {
                    firstName: 'Jay',
                    lastName: 'wang'
                }
            }
        }
    }
   

```js
页面结果:Jay

解释:子组件有默认的内容wang,如果不传参默认是wang,子组件user作为slot的一个attribute绑定上去,这被称为插槽prop,现在父级作用域可以在带值得slot提供一个名字也就是scope,scope是我们随便起的名字,随便起名字。

4.解构插槽prop

如上我们随便起了个名字,当然我们也可以不起名字就解构一下,还是用上面得例子只是父组件改变一下

   //父组件father
   <template>
       <sonslot>
           <template v-slot:header = {user}>
               {{user.firstName }}
           </template>
       </sonslot>
   </template>

解释:我们就直接能解构出来user,直接使用 当然我们也可以起别名

   //父组件father
   <template>
       <sonslot>
           <template v-slot:header = {user: person}>
               {{person.firstName }}
           </template>
       </sonslot>
   </template>

你甚至可以定义后备内容,用于插槽 prop 是 undefined 的情形:

   //父组件father
   <template>
       <sonslot>
           <template v-slot:header = {user = {firstName:'备用名'}}>
               {{user.firstName }}
           </template>
       </sonslot>
   </template>

解释:当我们子组件不绑定:user="user"时,就是undefined,这样我们父组件输出结果就是'备用名'

5.具名插槽缩写

v-slot:header可以缩写成#header 注意:如果我们父组件需要用到子组件的user,即使是没有名的插槽也要#default="{user}",不能写成#="{user}" 注意:默认插槽的缩写语法不能和具名插槽混用,因为它会导致作用域不明确,如下

//父组件
<!-- 无效,会导致警告 -->
<sonslot v-slot="scope">
  {{ scope.user.firstName }}
  <template v-slot:other="otherscope">
    这样是不被允许的
  </template>
</sonslot>

所以要完整写全template语法

// 父组件
<sonslot>
  <template v-slot:default="scope">
    {{scope.user.firstName }}
  </template>

  <template v-slot:other="otherscope">
    这样是被允许的
  </template>
</sonslot>

以上是对插槽的总结