vue插槽

2023年03月29日 未雨晴空 1评论 1759阅读 1喜欢

内容插槽

插槽无默认内容

<template>
  <div>
    <child>
      <h1>Hello World!</h1>
    </child>
  </div>
</template>

<script>
import Child from './child'
export default {
  name: 'Parent',
  components: {Child}
}
</script>

<style>
</style>
<template>
  <div>
    <header>
      <slot></slot>
    </header>
    <main>
      <slot></slot>
    </main>
    <footer>
      <slot>
      </slot>
    </footer>
  </div>
</template>

<script>
export default {
  name: 'Child'
}
</script>

<style>
</style>
Hello World!
Hello World!
Hello World!

通过上面的例子可以看到,child子组件定义了多个插槽,但是都没给插槽设置name,也就是后面会说到的具名插槽,父组件使用子组件时设置的内容在每个插槽位置都会显示。因此默认插槽只能有一个,有多个的话需要使用具名插槽

部分插槽有默认内容

<template>
  <div>
    <child>
      <h1>Hello World!</h1>
    </child>
  </div>
</template>

<script>
import Child from './child'
export default {
  name: 'Parent',
  components: {Child}
}
</script>

<style>
</style>
<template>
  <div>
    <header>
      <slot name="header">学生管理系统</slot>
    </header>
    <main>
      <slot>中间内容</slot>
    </main>
    <footer>
      <slot name="footer">
        xxxxxxx大学
      </slot>
    </footer>
  </div>
</template>

<script>
export default {
  name: 'Child'
}
</script>

<style>
</style>
学生管理系统
Hello World!
xxxxxxx大学

通过上面的例子可以看出,父组件使用时不指定插槽name的话即是默认插槽,且会覆盖默认插槽的默认内容。

具名插槽

<template>
  <div>
    <child>
      <h1 slot="header">班级管理系统</h1>
      <template #main>Hello World!</template>
      <template v-slot:footer>
        <h1>XXXXX班级</h1>
      </template>
    </child>
  </div>
</template>

<script>
import Child from './child'
export default {
  name: 'Parent',
  components: {Child}
}
</script>

<style>
</style>
<template>
  <div>
    <header>
      <slot name="header">学生管理系统</slot>
    </header>
    <main>
      <slot name="main">中间内容</slot>
    </main>
    <footer>
      <slot name="footer">
        xxxxxxx大学
      </slot>
    </footer>
  </div>
</template>

<script>
export default {
  name: 'Child'
}
</script>

<style>
</style>
班级管理系统
Hello World!
XXXXX班级

上面有三种方式完成的具名插槽的实现,其中#mainv-slot:"main"是vue 2.6.0新增的#mainv-slot:"main"的缩写形式

需要注意的是,v-slot和. # 指令必须放在<template>标签上,不然会报错。这是因为需要<template>标签来用作渲染内容的容器。

作用域插槽

如果子组件中存在数据,那么这些数据也可以通过作用域插槽传递回父组件。这可以通过在插槽中使用一定的语法来实现。当子组件中的数据更新时,父组件将自动接收这些更新。

比如现在想实现一个展现三国人物刘关张的历史信息页面,但是关于刘关张的人物信息需要请求接口拿到任务的信息,同时展现人物的信息形式也是待定的。这时候就需要借助作用域插槽来实现子组件的数据传回父组件了。

子组件

<template>
  <div>
    <header v-if="$scopedSlots.header">
      <h1>header</h1>
      <ul>
        <li
          v-for="(item,index) in dataList"
          :key="index"
        >
          <div>{{item.name}}</div>
          <slot
            :item="item"
            name="header"
          ></slot>
        </li>
      </ul>

    </header>
    <main v-if="$scopedSlots.main">
      <h1>main</h1>
      <ul>
        <li
          v-for="(item,index) in dataList"
          :key="index"
        >
          <div>{{item.name}}</div>
          <slot
            :item="item"
            name="main"
          ></slot>
        </li>
      </ul>
    </main>
    <footer v-if="$scopedSlots.footer">
      <h1>footer</h1>
      <ul>
        <li
          v-for="(item,index) in dataList"
          :key="index"
        >
          <div>{{item.name}}</div>
          <slot
            :item="item"
            name="footer"
          ></slot>
        </li>
      </ul>
    </footer>
  </div>
</template>

<script>
export default {
  name: 'Child',
  props: {
    dataList: Array
  },
  mounted() {
  }
}
</script>

<style scoped>
ul li {
  display: flex;
  justify-content: space-between;
  width: 200px;
}
</style>

父组件

<template>
  <div>
    <!-- 旧的用法 -->
    <child :dataList="dataList">
      <button
        slot="header"
        slot-scope="scope"
        @click="show(scope.item)"
      >
        查看人物
      </button>
    </child>

    <!-- v-slot用法 -->
    <child :dataList="dataList">
      <template v-slot:main="scope">
        <button @click="show(scope.item)">
          查看人物
        </button>
      </template>
    </child>

    <!-- v-slot的缩写用法 -->
    <child :dataList="dataList">
      <template #footer="{item1:item}">
        <button @click="show(item)">
          查看人物
        </button>
      </template>
    </child>

    <!-- 解构插槽props -->
    <child :dataList="dataList">
      <template #footer="{item}">
        <button @click="show(item)">
          查看人物
        </button>
      </template>
    </child>

    <!-- 解构插槽props同时更换别名 -->
    <h1>#footer="scope" 形式</h1>
    <child :dataList="dataList">
      <template #footer="{item:itemm}">
        <button @click="show(itemm)">
          查看人物
        </button>
      </template>
    </child>

    <!-- 解构插槽props设置默认值 -->
    <h1>#footer="scope" 形式</h1>
    <child :dataList="dataList">
      <template #footer="{newItem={name:'诸葛亮',value:'4'}}">
        <button @click="show(newItem)">
          查看人物
        </button>
      </template>
    </child>
  </div>
</template>

<script>
import Child from './child'
export default {
  name: 'Parent',
  components: {Child},
  data() {
    return {
      dataList: [{name: '刘备', value: '1'}, {name: '关羽', value: '2'}, {name: '张飞', value: '3'}]
    }
  },
  methods: {
    show(item) {
      console.log(item)
    }
  }
}
</script>

<style>
</style>

输出结果

header
刘备 查看人物
关羽 查看人物
张飞 查看人物
main
刘备 查看人物
关羽 查看人物
张飞 查看人物
footer
刘备 查看人物
关羽 查看人物
张飞 查看人物
footer
刘备 查看人物
关羽 查看人物
张飞 查看人物
footer
刘备 查看人物
关羽 查看人物
张飞 查看人物
footer
刘备 查看人物
关羽 查看人物
张飞 查看人物

这里需要注意下$slots$scopedSlots的区别在于$scopedSlots包含作用域插槽和具名插槽。而$slots只包含具名插槽。

动态插槽

子组件

<template>
  <div>
    <header v-if="$scopedSlots.header">
      <h1>header</h1>
      <ul>
        <li
          v-for="(item,index) in dataList"
          :key="index"
        >
          <div>{{item.name}}</div>
          <slot
            :item="item"
            name="header"
          ></slot>
        </li>
      </ul>

    </header>
    <main v-if="$scopedSlots.main">
      <h1>main</h1>
      <ul>
        <li
          v-for="(item,index) in dataList"
          :key="index"
        >
          <div>{{item.name}}</div>
          <slot
            :item="item"
            name="main"
          ></slot>
        </li>
      </ul>
    </main>
    <footer v-if="$scopedSlots.footer">
      <h1>footer</h1>
      <ul>
        <li
          v-for="(item,index) in dataList"
          :key="index"
        >
          <div>{{item.name}}</div>
          <slot
            :item="item"
            name="footer"
          ></slot>
        </li>
      </ul>
    </footer>
  </div>
</template>

<script>
export default {
  name: 'Child',
  props: {
    dataList: Array
  },
  mounted() {
  }
}
</script>

<style scoped>
ul li {
  display: flex;
  justify-content: space-between;
  width: 200px;
}
</style>

父组件

<template>
  <div>
    <!-- 动态插槽 -->
    <child :dataList="dataList">
      <template #[slotName]="{item}">
        <button @click="show(item)">
          查看人物
        </button>
      </template>
    </child>
  </div>
</template>

<script>
import Child from './child'
export default {
  name: 'Parent',
  components: {Child},
  data() {
    return {
      dataList: [{name: '刘备', value: '1'}, {name: '关羽', value: '2'}, {name: '张飞', value: '3'}],
      slotName: 'main'
    }
  },
  methods: {
    show(item) {
      console.log(item)
    }
  }
}
</script>

<style>
</style>
main
刘备 查看人物
关羽 查看人物
张飞 查看人物
  1. 2024-05-15 17:05:45

    这篇文章写得深入浅出,让我这个小白也看懂了!

    回复

发表评论 取消回复

电子邮件地址不会被公开。

请输入以http或https开头的URL,格式如:https://oneisall.top