vue插槽slot

140 阅读3分钟

应用场景

我们在构建页面过程中一般会把用的比较多的公共的部分抽取出来作为一个单独的组件,但是在实际使用这个组件的时候却又不能完全的满足需求,我希望在这个组件中添加一点东西,这时候我们就需要用到插槽来分发内容。

插槽分为默认插槽,具名插槽和作用域插槽。

初识插槽

//父组件
<template>
    <div>
      <div>大家好我是父组件</div>
      <myslot>
        <p>测试一下把内容写在这里了能否显示</p>
      </myslot>
    </div>
</template>

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

//子组件
<template>
  <div>
      <div>我是子组件</div>
      <p>现在测试一下slot</p>
      <slot></slot>
  </div>
</template>

测试结果

大家好我是父组件
我是子组件
现在测试一下slot
测试一下把内容写在这里了能否显示

总结

1.父组件在引用子组件时希望向子组价传递模板内容

2.子组件让父组件传过来的模板内容在所在的位置显示 

3.子组件中的<slot>就是一个槽,可以接收父组件传过来的模板内容,<slot> 元素自身将被替换

4.<myslot></myslot>组件没有包含一个 <slot> 元素,则该组件起始标签和结束标签之间的任何内容都会被抛弃

插槽分类

默认插槽

我们可能希望这个 <button> 内绝大多数情况下都渲染文本“Submit”,但是有时候渲染别的文本

//子组件
<button type="submit">
  <slot>Submit</slot>
</button>

//父组件
<submit-button></submit-button>

结果:渲染文本“Submit”

//父组件
<submit-button> Save</submit-button>

结果:渲染文本“Save”

总结

父组件不提供内容,渲染默认插槽内容,如提供内容,渲染提供的内容。

具名插槽

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

//父组件
<template>
  <myslot>
    <div>大家好我是父组件</div>
    <template #header>
      <h1>Here might be a page title</h1>
    </template>

    <p>A paragraph for the main content.</p>
    <p>And another one.</p>

    <template #footer>
      <p>Here's footer info</p>
    </template>
  </myslot>
</template>

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

测试结果

Here might be a page title  

大家好我是父组件
A paragraph for the main content.
And another one.

Here's footer info

总结

1.<slot> 元素有一个特殊的 attribute:name。这个 attribute 可以用来定义额外的插槽

2.父组件中会向子组件中具名传递对应的模板内容,而没有指定名字的模板内容会传递给子组件中不带 name<slot>,一个不带 name<slot> 出口其实带有隐含的名字“default”,所以#default也会传递给子组件中不带 name<slot>

3.父组件在向具名插槽提供内容的时候,我们可以在一个 <template> 元素上使用 v-slot 指令,并以 v-slot 的参数的形式提供其名称,如v-slot:header 或 #header

作用域插槽

这里主要解决的是父组件在向子组件插槽传递模板内容时存在访问子组件数据的问题 

//子组件
<template>
  <div>
    <span>
      <slot>{{user.firstName}}</slot>
    </span>
  </div>
</template>

<script>
  export default {
    data () {
      return {
        user:{
          firstName:'gerace',
          lastName:'haLi'
        }
      }
    }
  }
</script>

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

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

运行代码时会提示报错

原因:父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的

解决方法:我们可以将 user 作为 <slot> 元素的一个 attribute 绑定上去,让 user 在父级的插槽内容中可用,将包含所有插槽 prop 的对象命名为 scope,你也可以使用任意你喜欢的名字。

//子组件
<template>
  <div>
    <span>
      <slot v-binduser="user">{{user.firstName}}</slot>
    </span>
  </div>
</template>

<script>
  export default {
    data () {
      return {
        user:{
          firstName:'gerace',
          lastName:'haLi'
        }
      }
    }
  }
</script>

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

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

多个插槽的情况

//子组件
<template>
  <div>
    <span>
      <slot v-bind:userData="user" name="header">
        {{ user.firstName }}
      </slot>
      <slot v-bind:hobbyData="hobby" name="footer">
        {{ hobby.fruit }}
      </slot>
    </span>
  </div>
</template>

<script>
  export default {
    data () {
      return {
        user:{
          firstName: 'gerace',
          lastName: 'haLi',
        },
        hobby:{
          fruit: "apple",
          color: "blue"
        }
      }
    }
  }
</script>

//父组件
<template>
  <myslot>
      <template v-slot:header="scope">
        {{ scope.userData.firstName }}
      </template>
      <template v-slot:footer="scope">
        {{ scope.hobbyData.fruit }}
      </template>
  </myslot>
</template>

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

或使用解构

//父组件
<template>
  <myslot>
      <template v-slot:header="{userData}">
        {{ userData.firstName }}
      </template>
      <template v-slot:footer="{hobbyData}">
        {{ hobbyData.fruit }}
      </template>
  </myslot>
</template>

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

或使用简写

<template>
  <myslot>
      <template #header="{userData}">
        {{ userData.firstName }}
      </template>
      <template #footer="{hobbyData}">
        {{ hobbyData.fruit }}
      </template>
  </myslot>
</template>

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

注意: 默认插槽的缩写语法不能和具名插槽混用,因为它会导致作用域不明确

<template>
  <myslot v-slot="scope">
     {{ scope.user.firstName }}
     <template v-slot:other="otherscope">
   		scope is NOT available here
     </template>
  </myslot>
</template>