Vue2插槽

269 阅读4分钟

插槽概念

  • 插槽(Slot)Vue 为组件的封装者提供的能力
  • 在封装组件时,把不确定的、希望由用户指定的部分定义为插槽
  • 目的: 让组件更加具有扩展性
  • 分类: 默认插槽,具名插槽,作用域插槽

默认插槽

  • Vue 实现了一套内容分发的 API,将 <slot> 元素作为承载分发内容的出口
  • 作用: 在父组件中可以向子组件插槽位置插入模板结构
  • 示例: 定义插槽
 <div class="main">
   <slot></slot> <!-- 插槽位置 -->
 </div>
  • 应用场景: 比如在下面的图片中,篮球项展示球员名字,游戏项展示游戏名称,电影项展示电影名称,用 v-show 实现难免有些繁琐,此时可以使用插槽

image-20221114105613901.png

  • ①在子组件 Child 中定义一个插槽
 <div class="child">
   <h1>{{title}}</h1>
   <!-- 定义插槽,等待使用者填充 -->
   <slot>默认值当使用者没有传递具体结构时展示</slot>
 </div>
  • ②在父组件 App 中的 <Child> 标签下插入图片,没有传递结构的标签就展示插槽的默认内容
 <!-- 篮球项 -->
 <Child title="篮球">
   <ul>
     <li>迈克尔·乔丹</li>
     <li>勒布朗·詹姆斯</li>
   </ul>
 </Child>
 <!-- 游戏项 -->
 <Child title="游戏">
   <ul>
     <li>英雄联盟</li>
     <li>穿越火线</li>
   </ul>
 </Child>
 <!-- 电影项 -->
 <Child title="电影">
   <ul>
     <li>人生大事</li>
     <li>万里归途</li>
   </ul>
 </Child>
 <!-- 没有传递 -->
 <Child />

具名插槽

  • 带有名字的插槽

  • 作用: 可以将不同的结构,插入不同的插槽中

  • 场景: 子组件中多个结构可能都会被更换,就可用具名插槽

  • 示例: 如果想放多个结构,而使用的是默认插槽,那么当 Vue 在解析模板的时候,就会把组件标签包含的所有内容都填充到插槽里面,有多少个默认插槽就放多少份,如下面所示:

    • ①在 Child 组件中放两个默认插槽
     <div class="child">
       <h1>{{ title }}</h1>
       <slot>默认值当使用者没有传递具体结构时展示</slot>
       <slot>默认值当使用者没有传递具体结构时展示</slot>
     </div>
    
    • ②查看页面,发现在 App 组件中 <Child> 标签内的结构都渲染了两份

image-20221114111107658.png

基本使用

  • 鉴于以上情况,可以使用具名插槽来解决,往规定的插槽填充特定的结构

    • ①定义具名插槽,在 Child 组件中定义两个具名插槽
     <div class="child">
       <h1>{{ title }}</h1>
       <slot name="center">默认值当使用者没有传递具体结构时展示</slot>
       <slot name="footer">默认值当使用者没有传递具体结构时展示</slot>
     </div>
    
    • ②使用具名插槽,在 <Child> 标签中,给特定的结构添加 slot 属性,值是插槽的名字
     <Child title="篮球">
       <h3 slot="center">勒布朗詹姆斯</h3>
       <a href="#" slot="footer">球员简介</a>
     </Child>
    

image-20221114112250344.png

注意:slot属性在2.60之后已经废弃,但未移除

v-slot的使用

  • 如果要把一堆内容填充到指定名称的插槽中,需要使用 <template> 标签进行包裹,可以使用 v-slot
  • 用法: v-slot:插槽名称

注意: v-slot指令不能直接用在元素身上,必须用在template标签或组件标签

 <Child title="篮球">
   <template v-slot:center>
     <h3>勒布朗詹姆斯</h3>
   </template>
   <template v-slot:footer>
     <a href="#" slot="footer">球员简介</a>
   </template>
 </Child>
  • 简写: v-slot:的简写形式是 #
 <template #footer>
   <a href="#" slot="footer">球员简介</a>
 </template>

动态插槽名

 <Child title="篮球">
   <template v-slot:[slotName]>
     <h3>勒布朗詹姆斯</h3>
   </template>
 </Child>

作用域插槽

  • 让插槽内容能够访问子组件中才有的数据
  • 示例: 带有如下模板的 <Child> 组件
 <div class="child">
    <slot>{{ user.lastName }}</slot>
 </div>
 export default {
   data() {
     return {
       user: { firstName: "LeBron", lastName: "James" },
     };
   },
 };
  • 后续可能想换掉备用内容,用名而非姓来显示,但下面代码不会生效并且会报错
  • 因为只有 <Child> 组件可以访问到 user,而提供的内容是在父级渲染的
 <Child>
   {{ user.firstName }}
 </Child>

image-20221114114551371.png

编译作用域

  • 父子组件是有不同的模板各自独立的作用域,组件在实例化时作用域都是孤立的

那么使用子组件时,子组件中嵌套的内容是在哪个作用域里编译呢?

  • 使用单文件组件进行演示
 <div id="app">
     <cpn v-show ="isShow"></cpn> <!--使用isShow-->
 </div>
 ​
 <template id="cpn">
   <div>
     <h2>我是cpn组件</h2>
     <p>我是组件哈哈哈</p>
     <button v-show="isShow">按钮</button> <!--使用isShow-->
   </div>
 </template>
 <script>
   const app = new Vue({
     el:'#app',
     data:{
       message:'你好啊',
       isShow:true  //vue实例中的isShow为true
     },
     components:{
       cpn:{
         template:'#cpn',
         data(){
           return {
             isShow:false // cpn组件中的isShow为false
           }
         }
       }
     }
   })
 </script>

image-20221114115207390.png

  • 可以看到 cpn 组件被渲染了,而 cpn 组件中的按钮并没有显示
  • 证明父组件模板的内容在父组件作用域内编译,子组件模板的内容在子组件作用域内编译

基本使用

  • 作用域插槽其实就是带数据的插槽
  • 子组件提供给父组件的参数仅限于插槽中使用
  • 作用: 父组件替换插槽的标签,但是内容由子组件来提供
  • 示例: 回归以上问题,解决父组件中使用子组件的数据,为了让 user 在父级的插槽内容中可用,可以将 user 作为 <slot> 元素的一个 attribute 绑定
 <div class="child">
   <slot :user="user">{{ user.lastName }}</slot>
 </div>
  • 绑定在 <slot> 元素上的 attribute 被称为插槽 prop
  • 现在可以使用带值的 v-slot 来定义提供的插槽 prop 的名字
 <Child>
   <template #default="slotProps">
     <h1>{{ slotProps.user.firstName }}</h1>
   </template>
 </Child>

image-20221114120226270.png

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

image-20221114120613728.png

  • 只要出现多个插槽,需要为所有的插槽使用完整的基于 <template> 的语法
 <Child>
   <template v-slot="slotProps">
     {{ slotProps.user.firstName }}
   </template>
   <template v-slot:other="otherSlotProps">
     {{ otherSlotProps }}
   </template>
   ...
 </Child>

解构插槽 Prop

  • 作用域插槽的内部工作原理是将插槽内容包裹在一个拥有单个参数的函数里
 function (slotProps) {
   // 插槽内容
 }
  • 这代表 v-slot 的值可以是任何能够作为函数定义中参数的 JavaScript 表达式
  • 也就是说可以对其进行解构
 <template v-slot="{user}">
   {{ slotProps.user.firstName }}
 </template>
  • 相当于给函数的参数进行了解构
 function ({user}) {}