首页 > 其他分享 >vue插槽

vue插槽

时间:2023-06-27 17:46:52浏览次数:50  
标签:vue 插槽 html 小土豆 复制 Child 组件

什么是插槽

在日常的项目开发中,当我们在编写一个完整的组件时,不可避免的会引用一些外部组件或者自定义组件

有了这种引用关系之后,我们就可以把它们称为父组件或者子组件,同时父子组件之间有很多的通信方式,比如可以通过props子组件传递数据,或者通过$emit$parent调用父组件中的方法。

下面就是一个非常简单的父组件引用子组件的例子。

html复制代码<!-- 子组件: /slot-demo/src/components/Child.vue -->
<template>
  <div class="child">
  
    <!-- 标记 -->
    <div>
      <i class="el-icon-s-flag"></i>Badge 标记
      <div class="content">
        <el-badge :value="12" class="item">
          <el-button size="small">评论</el-button>
        </el-badge>
      </div>
    </div>
   
    <!-- 进度条 -->
    <div>
      <i class="el-icon-s-flag"></i>进度条
      <div class="content">
        <el-progress :percentage="50"></el-progress>
      </div>
    </div>
    
  </div>
</template>

<!-- 省略其它代码 -->

接着我们在App组件中引用Child组件。

html复制代码<!-- 父组件: /slot-demo/src/App.vue -->
<template>
  <div id="app">
    <!-- 使用子组件 -->
    <child></child>
  </div>
</template>
<script>
import Child from './components/Child.vue'
export default {
  name: 'App',
  components: {
    Child
  }
}
</script>

最后运行项目,子组件的内容成功被引用并展示在页面上。

那假如我们现在有这样一个需求:在引用Child组件的同时,希望在Child组件的指定位置插入一段内容:<h1> 欢迎大家关注小土豆 </h1>

如果我们直接将内容写入<child></child>内部,是不会生效的。

html复制代码<!-- 父组件: /slot-demo/src/App.vue -->
<template>
  <div id="app">
    <!-- 使用子组件 -->
    <child>
      <h1> 欢迎大家关注小土豆 </h1>
    </child>
  </div>
</template>
<script>
import Child from './components/Child.vue'
export default {
  name: 'App',
  components: {
    Child
  }
}
</script>

可以看到并未达到预期效果:

那为了解决类似这样的问题,Vue就设计出来了slot这个东西。slot翻译过来叫做插槽,也可称其为Vue的内容分发机制,它的主要作用就是向子组件指定位置插入一段内容,这个内容可以是HTML或者其他的组件

默认插槽

在前面一节内容里,我们提出了一个需求:在引用Child组件的同时,希望在Child组件的指定位置插入一段内容:<h1> 欢迎大家关注小土豆 </h1>

那这个需求如何使用插槽来实现呢?我们来实践一下。

首先我们需要在子组件中写入<slot></slot>,同时这个在<slot>标签内部可以有默认的内容,比如<slot>我是这个slot里面本来的内容</slot>

html复制代码<!-- 子组件: /src/components/Child.vue -->
<template>
  <div class="child">
  
    <!-- 标记 -->
    <div>
      <i class="el-icon-s-flag"></i>Badge 标记
      <div class="content">
        <el-badge :value="12" class="item">
          <el-button size="small">评论</el-button>
        </el-badge>
      </div>
    </div>
   
    <!-- 进度条 -->
    <div>
      <i class="el-icon-s-flag"></i>进度条
      <div class="content">
        <el-progress :percentage="50"></el-progress>
      </div>
    </div>
    <!-- 占位符 -->
    <slot>我是这个slot里面本来的内容</slot>
    
  </div>
</template>

<!-- 省略其它代码  -->

接着就是在父组件中传入我们希望插入到子组件中的内容。

html复制代码<!-- 父组件: /src/App.vue -->
<template>
  <div id="app">
    <!-- 使用子组件 -->
    <child>
      <h1> 欢迎大家关注小土豆 </h1>
    </child>
  </div>
</template>
<script>
import Child from './components/Child.vue'
export default {
  name: 'App',
  components: {
    Child
  }
}
</script>

此时在运行项目,就能看到<h1> 欢迎大家关注小土豆 </h1>这段内容已经成功的显示在页面上。

具名插槽

具名插槽就是给我们的插槽起一个名字,即给<slot></slot>定义一个name属性。

html复制代码<!-- 插槽名称为:heading -->
<slot name="heading"></slot>

<!-- 插槽名称为:sub-heading -->
<slot name="sub-heading"></slot>

<!-- 插槽名称为:footer-text -->
<slot name="footer-text"></slot>        

插槽起了名称以后,我们在父组件中就可以使用v-slot:name或者#name往指定的插槽填充内容。

#namev-slot:name的简写形式

下面我们就来实践一下具名插槽

首先是在子组件(Child.vue)中定义具名插槽

html复制代码<!-- 子组件: /slot-demo/src/components/Child.vue -->
<template>
  <div class="child">
    <!-- 插槽名称为:heading -->
    <slot name="heading"></slot>     
    <!-- 插槽名称为:sub-heading -->
    <slot name="sub-heading"></slot> 
    <!-- 标记 -->
    <div>
      <i class="el-icon-s-flag"></i>Badge 标记
      <div class="content">
        <el-badge :value="12" class="item">
          <el-button size="small">评论</el-button>
        </el-badge>
      </div>
    </div>
    
    <!-- 进度条 -->
    <div>
      <i class="el-icon-s-flag"></i>进度条
      <div class="content">
        <el-progress :percentage="50"></el-progress>
      </div>
    </div>
    <!-- 插槽名称为:footer-text -->
    <slot name="footer-text"></slot>        
 
  </div>
</template>

<!-- 省略其它代码 -->

接着在父组件(App.vue)中使用。

html复制代码<!-- 父组件: /slot-demo/src/App.vue -->
<template>
  <div id="app">
    <!-- 使用子组件 -->
    <child>
      <template v-slot:heading>
        <h1>element-ui组件</h1>
      </template>
      <template v-slot:sub-heading>
        <p>这里是element-ui的部分组件介绍</p>
      </template>
      <template v-slot:footer-text>
        <p>出品@小土豆</p>
      </template>
    </child>
  </div>
</template>
<script>
import Child from './components/Child.vue'
export default {
  name: 'App',
  components: {
    Child
  }
}
</script>

运行项目就能看到对应的内容被插入到对应的插槽内:

补充内容——默认插槽的name属性

其实关于前面的默认插槽它也是有name属性的,其值为default,所以在父组件中也可以这样写:

html复制代码<!-- 父组件: /slot-demo/src/App.vue -->
<child>
  <template v-slot:defalut>
    <h1> 欢迎大家关注小土豆 </h1>
  </template>
</child>

补充内容——<template> 元素上使用 v-slot 指令

在演示具名插槽的时候,我们的v-slot是写在<template>元素上的,这个是比较推荐的写法,因为<template>在处理的过程中不会渲染成真实的DOM节点。

html复制代码
<template v-slot="default">
  <h1>欢迎关注小土豆</h1>
</template>

处理之后的DOM节点:

html复制代码<h1 data-v-2dcc19c8="">欢迎关注小土豆</h1>

当然我们也可以将v-slot应用在其他的HTML元素上,这样最终插入到子组件中的内容就会有一层真实的DOM节点包裹。

html复制代码<div class="text" v-slot="default">
  <h1>欢迎关注小土豆</h1>
</div>

处理之后的DOM节点:

html复制代码<div data-v-2dcc19c8="" class="text">
  <h1 data-v-2dcc19c8="">欢迎关注小土豆</h1>
</div>

作用域插槽

关于作用域插槽的相关概念和示例看了很多,但相对于前面两种类型的插槽来说,确实有些难以理解。如果需要用一句话去总结作用域插槽,那就是在父组件中访问子组件的数据,或者从数据流向的角度来讲就是将子组件的数据传递到父组件

一个新概念或者一个新技术的出现总是有原因的,那作用域插槽的出现又是为了解决什么样的问题呢?一起来研究一下吧。

作用域插槽的使用

我们先来看看如何利用作用域插槽实现在父组件中访问子组件的数据

首先我们需要在子组件的插槽<slot><slot>上使用v-bind绑定对应的数据。

html复制代码<!-- 子组件: /slot-demo/src/components/Child.vue -->
<template>
  <div class="child">
    <slot 
        name="heading" 
        v-bind:headingValue="heading">
        {{heading}}
    </slot>
    <!-- 为了让大家看的更清楚 已经将Child.vue组件中多余的内容删除 --> 
  </div>
</template>
<script>
export default {
  name: 'Child',
  data() {
    return {
        heading: '这里是默认的heading'
    }
  }
}
</script>

可以看到我们在<slot>上使用v-bind绑定了vue data中定义的heading数据。

接着我们就可以在父组件中定义一个变量来接收子组件中传递的数据。

父组件中接收数据的变量名可以随意起,这里我起的变量名为slotValue

html复制代码<!-- 父组件: /slot-demo/src/App.vue -->
<template>
  <div id="app">
    <child>
      <template v-slot:heading="slotValue" >
        <h1>element-ui组件</h1>
        slotValue = {{slotValue}}
      </template>
    </child>
  </div>
</template>

运行项目后查看页面的结果:

可以看到slotValue是一个对象,保存了一组数据,其就是我们在子组件<slot>上使用v-bind绑定的属性名headingValue,其v-bind绑定的heading值。

作用域插槽的应用场景

前面我们了解了作用域插槽的用法,也得知其主要目的是为了能在父组件中访问子组件的数据。那什么时候父组件需要访问子组件的数据呢。

我们来举个简单的栗子。

假设我们有下面这样一个Card组件:

html复制代码<!-- Card组件:/slot-demo/src/components/Card.vue -->
<template>
  <div class="card">
    <h3>{{title}}</h3>
    <p v-for="item in list" :key="item.id">
      {{item.id}}.{{item.text}}
    </p>
  </div>
</template>
<script>
export default {
  name: 'Card',
  props: ['title', 'list'],
  data() {
    return {
    }
  }
}
</script>
<style scoped>
  .list{
    border: 1px solid;
    padding: 20px;
  }
  .list p{
    border-bottom: 2px solid #fff;
    padding-bottom: 5px;
  }
</style>

其中Card组件中展示的titlelist数据由父组件传入。

接着在App组件中复用Card组件,并且传入titlelist数据。

html复制代码<!-- App组件:/slot-demo/src/App.vue -->
<template>
  <div id="app">
    <card :list="list" :title="title">
    </card>
  </div>
</template>
<script>
import Card from './components/Card.vue'
export default {
  name: 'App',
  components: {
    Card,
  },
  data() {
    return {
      title: '名人名言',
      list:[
        {
          id:1,
          text:'要成功,先发疯,头脑简单向前冲'
        },{
          id:2,
          text:'不能天生丽质就只能天生励志!'
        },{
          id:3,
          text:'世上唯一不能复制的是时间,唯一不能重演的是人生。'
        }
      ]
    }
  }
}
</script>

<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  /* text-align: center; */
  /* color: #2c3e50; */
  /* margin-top: 60px; */
  color: #fff;
  background: rgba(232, 0, 0, 0.3);
  padding: 20px;
}
</style>

运行项目查看页面:

Card组件本身并不复杂,就是展示titlelist里面的数据。但凡是有相同需求的都可以通过复用Card组件来实现。

但是仔细去想,我们的Card组件其实并没有那么灵活:如果有些页面需要复用该组件,但是希望在title处增加一个图标;或者有些页面需要在展示内容时候不显示编号1、2、3

那这样的需求使用插槽就可以轻松实现。

html复制代码<!-- Card组件:/slot-demo/src/components/Card.vue -->
<template>
  <div class="card">
    <h3>
      <slot name="title" v-bind:titleValue="title"> {{title}} </slot>
    </h3>
    <p v-for="item in list" :key="item.id">
      <slot name="text" v-bind:itemValue="item">{{item.id}}.{{item.text}}</slot>
    </p>
  </div>
</template>

我们在Card组件展示titlelist的位置分别添加了对应的具名插槽,并且通过v-bindtitleitemlist循环出来的数据)传递给了父组件

此时父组件就可以控制子组件的显示。

假如我们需要在title处添加图标,则App组件复用Card组件的方式如下:

html复制代码<!-- App组件:/slot-demo/src/App.vue -->
<card :list="list" :title="title">
  <template v-slot:title="slotTitle">
    <i class="el-icon-guide"></i>{{slotTitle.titleValue}}
  </template>
</card>

页面效果:

亦或者有些页面需要在展示内容时候不显示编号1、2、3

html复制代码<!-- App组件:/slot-demo/src/App.vue -->
<card :list="list" :title="title">
  <template v-slot:text="slotItem">
    {{slotItem.itemValue.text}}
  </template>
</card>

页面效果:

这里应该能想起来element table组件的实现方式,是不是也有点这样的意思呢

到这里或许有人会说这样的需求不用插槽也能实现,直接在Card组件中增加一些逻辑即可。这样的说法固然是可以实现功能,但是显然不是一个好办法。

因为组件的设计本身是希望拿来复用的,如果这个组件本身大部分实现是符合我们的需求的,只有一小部分不符合,我们首先应该想要的是去扩展该组件,而不是修改组件,这也是软件设计的思想:开放扩展,关闭修改。所以插槽的出现正是对组件的一种扩展,让我们可以更加灵活的复用组件。

废弃的插槽语法

关于以上所描述的插槽语法,均是vue 2.6.0以后的语法。在这之前,插槽的语法为slot(默认插槽或者具名插槽)slot-scope(作用域插槽)

默认插槽

html复制代码<!-- Card组件:/slot-demo/src/components/Card.vue -->
<!-- 子组件的写法依然不变 -->
<slot></slot>

<!-- App组件:/slot-demo/src/App.vue -->
<child>
  <template>
    <h1>欢迎关注小土豆</h1>
  </template>
<child>
<p>或者</p>
<child>
  <template slot="default">
    <h1>欢迎关注小土豆</h1>
  </template>
<child>

页面效果:

具名插槽

html复制代码<!-- Card组件:/slot-demo/src/components/Card.vue -->
<!-- 子组件的写法依然不变 -->
<!-- 插槽名称为:heading -->
<slot name="heading"></slot>   
<!-- 插槽名称为:sub-heading -->
<slot name="sub-heading"></slot> 
<!-- 插槽名称为:footer-text -->
<slot name="footer-text"></slot> 

<!-- App组件:/slot-demo/src/App.vue -->
<child>
  <template slot="heading">
    <h1>element-ui组件</h1>
  </template>
  <template slot="sub-heading">
    <p>这里是element-ui的部分组件介绍</p>
  </template>
  <template slot="footer-text">
    <p>出品@小土豆</p>
  </template>
</child>

页面效果:

作用域插槽

html复制代码<!-- 子组件: /src/components/Child.vue -->
<slot 
    name="heading" 
    v-bind:headingValue="heading">
    {{heading}}
</slot>

<!-- 父组件: /slot-demo/src/App.vue -->
<child>
  <template slot="heading" slot-scope="headingValue" >
    <h1>element-ui组件</h1>
    headingValue = {{headingValue}}
  </template>
</child>

页面效果:

总结

到这里本篇文章就结束了,内容非常简单易懂,可以是茶余饭后的一篇知识回顾。

最后我们在来做一个小小的总结:

写在最后

原作者: 小土豆
参考链接:点这里哦!

标签:vue,插槽,html,小土豆,复制,Child,组件
From: https://www.cnblogs.com/fairya/p/17509512.html

相关文章

  • VUE框架组件中通信方式(3)
    useAttrs使用方式类似于props传值,useAttrs既可以传数据,也可以传事件。如果父组件给子组件传递的参数,在子组件中同时使用props和useAttrs获取,那么参数只在props中生效,也就是说props的优先级高于useAttrs。示例代码如下://template><div><h1>useAttrs</h1><el-but......
  • vue中精确计算问题,出现很多位小数的问题与原因
    出现的原因计算机把小数转换成二级制,会出现无限循环的情况。再把无限循环的二级制转化成十进制的时候,变成了一个无限循环的数字。在处理双精度浮点数的小数部分最多支持52位,所以转换成十进制之后,就出现了很多位小数的存在。例如:0.1+0.2=0.300000000000000040.3-0.2=......
  • VUE 滚动到底部加载更多(附带指令实现方式)
    VUE滚动到底部加载更多(附带指令实现方式)直接上代码:mounted(){window.addEventListener('scroll',this.handleScroll,true);},destroyed(){window.removeEventListener('scroll',this.handleScroll);},methods:{......
  • 直播开发app,vue防抖 自定义ref实现输入框防抖
    直播开发app,vue防抖自定义ref实现输入框防抖 首先需要把input的双向绑定v-mode拆开为一个value和一个input事件,在事件里注册一个函数debUpdata,debUpdata里获取到input输入内容再赋值给text,这就类似于手写v-mode,代码如下: <template> <divclass="hello">  <inpu......
  • 基于vue+elementUI使用vue-amap高德地图
    首先,需要去高德地图进行注册一个https://lbs.amap.com/?ref=https://console.amap.com/dev/index,得到一个key然后安装依赖npminstallvue-amap—save在main.js中加入importVueAMapfrom'vue-amap’;Vue.use(VueAMap);VueAMap.initAMapApiLoader({key:'YOUR_KEY’......
  • vue-element-admin 动态路由踩坑之路。。。
    参考帖子1.菜单详解(主要是加载原理,还有一些脚本,json格式的参考)https://blog.csdn.net/weixin_44922964/article/details/120927244https://blog.csdn.net/qq_57581439/article/details/1278629972.三级路由:https://www.cnblogs.com/netcore-vue/p/14911375.html(这个主要是加载......
  • vue3透传 Attributes
    “透传attribute”指的是传递给一个组件,却没有被该组件声明为props或emits的attribute或者 v-on 事件监听器。最常见的例子就是 class、style 和 id当一个组件以单个元素为根作渲染时,透传的attribute会自动被添加到根元素上A组件:<template><h3>ComponentA</......
  • Vue2电商实战项目(六)个人中心
    个人中心Center组件先搞定静态组件###router.routes.jsimportCenterfrom'@/pages/Center'exportdefault[ { name:"center", path:"/center", component:Center, meta:{ show:true } }......拆分Center组件,把我的订单和团购订单拆分成两个子路......
  • 【vue2】使用vue常见的业务流程与实现思路
     ......
  • Vue如何在页面加载时将url的参数赋值给组件
    <template> <inputv-model="loginForm.username" name="username" type="text" tabindex="1" auto-complete="on" /><inputv-model="loginForm.password":type="passwordType"......