Vue插槽详解-作用域插槽

3 篇文章 0 订阅
1 篇文章 0 订阅

前言

在Vue的官方文档中对于插槽的描述比较凝练不容易理解,作者在刚开始接触插槽时也是一头雾水,在实践中好像也可以简单的使用,但是想把插槽用的得心应手就比较困难了。

最近因为工作需求需求手动封装一个类似ElementUI的级联选择器的组件,在开发过程中一直不太明白它的自定义节点功能:

<el-cascader :options="options">
  <template slot-scope="{ node, data }">
    <span>{{ data.label }}</span>
    <span v-if="!node.isLeaf"> ({{ data.children.length }}) </span>
  </template>
</el-cascader>

后来又去仔细研究了一下作用域插槽,最后终于成功的开发完成了,下面想和大家分享一下我通过开发这个组件对于插槽的一些新的理解,由于本人才疏学浅,所以有什么不妥的地方还希望各位大佬们指正。

下面我主要分为三部分讲解,默认插槽、具名插槽和作用域插槽,默认插槽和具名插槽比较容易理解所以下面不用解释太多,主要是分享一些对作用域插槽的理解。

默认插槽

默认插槽又被称为单个插槽、匿名插槽,默认插槽的使用是最方便的,只需要在子组件需要插入父组件传入内容的地方加上即可,下面通过一个简单的例子展示一下:

父组件:

<template>
    <div>
        <h1>这里是父组件</h1>
        <child>
            <h3>这里是传递的内容</h3>
        </child>
    </div>
</template>

子组件:

<template>
    <div>
        <h2>这里是子组件</h2>
        <slot></slot>
    </div>
</template>

渲染的dom:

<div>
    <h1>这里是父组件</h1>
    <div>
        <h2>这里是子组件</h2>
        <h3>这里是传递的内容</h3>
    </div>
</div>

效果图:


是不是很简单,但是默认插槽有个缺陷,你如果现在父组件中传递两部分内容并且分别渲染在不同的位置,这个默认插槽是做不到了,它会渲染所有传递的内容。像这样:
父组件:

    <div>
        <h1>这里是父组件</h1>
        <child>
            <h3>这里是头部插槽</h3>
            <h3>这里是底部插槽</h3>
        </child>
    </div>
</template>

子组件:

<template>
    <div>
        <h2>这里是子组件</h2>
        <p>这里是头部,下面是插槽</p>
        <slot></slot>
        <p>这里是底部,下面是插槽</p>
        <slot></slot>
    </div>
</template>

效果图:


可以发现传递的内容在子组件中全部渲染了,而且两部分一样,这并不是我们想要的,为了解决这种问题,也为了使组件能够更灵活的复用,具名插槽的价值就体现出来了,下面来介绍一下具名插槽。

具名插槽

具名插槽就是有名字的插槽,就和我们的名字一样,用来区分插入的位置,这样就解决我们上面的问题。
父组件:

<template>
    <div>
        <h1>这里是父组件</h1>
        <child>
            <h3 slot="head">这里是头部插槽</h3>
            <h3 slot="footer">这里是底部插槽</h3>
        </child>
        <!-- 自Vue2.6起,语法变成了这样 -->
        <child>
            <template v-slot:head>
                <h3>这里是头部插槽</h3>
            </template>
            <template v-slot:footer>
                <h3>这里是底部插槽</h3>
            </template>
        </child>
    </div>
</template>

子组件:

<template>
    <div>
        <h2>这里是子组件</h2>
        <p>这里是头部,下面是插槽</p>
        <slot name="head"></slot>
        <p>这里是底部,下面是插槽</p>
        <slot name="footer"></slot>
    </div>
</template>

效果图:

接下来主角要登场了,来揭开作用域插槽的神器面纱。

作用域插槽

官网是这样描述的:有时让插槽内容能够访问子组件中才有的数据是很有用的。显然作用于插槽的作用就是使父组件能够引用子组件中的数据。
下面用一个简单地例子展示一下:
父组件:

<template>
    <div>
        <h1>这里是父组件</h1>
        <child>
            <template slot-scope="slotsProps">
                {{slotsProps.info}}
            </template>
        </child>
        <!-- 自Vue2.6起,语法变成了这样 -->
        <child>
            <template v-slot:default="slotProps">
                {{slotProps.info}}
            </template>
        </child>
    </div>
</template>

子组件:

<template>
    <div>
        <h2>这里是子组件</h2>
        <slot :info="info"></slot>
    </div>
</template>

效果图:


可以看到成功渲染了子组件中的数据,可能有人会问既然要渲染子组件中的数据那直接写在子组件中就好了,何必通过作用域插槽传递数据给父组件然后再进行渲染了。

有这样一个场景,一个级联选择器,根据不同的应用场景渲染不同的变量,而且在不同的场景下可以进行不同而操作,比如编辑、删除等,那么想用这个组件是不是要对传入的数据的格式做严格的规定,这样组件的复用性是不是就变得很差了呢?但是通过作用域插槽就可以实现了呢。下面用一个简单地例子解释:

父组件:

<template>
  <div>
    <div class="container">
      <child :options="options" class="child">
        <template slot-scope="slotsProps">
          <span>{{ slotsProps.data.label }}</span>
          <button @click="getInfo(slotsProps.data)">编辑</button>
        </template>
      </child>
      <child :options="options" class="child">
        <template slot-scope="slotsProps">
          <span>{{ slotsProps.data.value }}</span>
          <button @click="getInfo(slotsProps.data)">删除</button>
        </template>
      </child>
    </div>
  </div>
</template>

子组件:

<template>
  <div>
    <div v-for="item in options" :key="item.value">
      <slot :data="item"></slot>
    </div>
  </div>
</template>

效果图:


惊喜的发现同样一个组件完全可以在不同的场景下复用,下面我们来看一下作用域插槽的数据传递。

作用于插槽的数据传递

在学习插槽时一直不太明白作用域插槽到底是什么,下面我总结了几点来理解作用域插槽。

  1. 插槽的slot写在哪里?作用域插槽和其他插槽一样,slot都是写在父组件中,slot是子组件暴露给父组件的接口,需要父组件通过slot传值给子组件。
  2. 作用域插槽,字面理解插槽的作用域为这个插槽,只对这个插槽生效。
  3. 父组件中引用数据时的属性名和子组件中赋值的属性名有什么关系?slot-scope="slotsProps"这里定义了插槽的内容使用slotsProps来承接;{{ slotsProps.data.label }}这里是引用传给插槽的数据data对象的label属性;<slot :data="item"></slot>这里把item对象赋值给data

注意:子组件中的data要和父组件中的data保持名称一致

那么上面这个例子的数据传递的步骤是

  1. 父组件将options传递给子组件
  2. 子组件把item传递给插槽,并暴露给父组件
  3. 父组件调用子组件插槽传递过来的数据

父组件调用子孙组件中的数据

有时我们发现在子组件中并拿不到要传给插槽的数据,可能要在子孙组件中的才可能拿到,这时我们应该怎么办呢?

demo.vue

<template>
  <div>
    <span>{{value}}</span>
    <div class="container">
      <parent :options="options" class="child">
        <template slot-scope="slotsProps">
          <span>{{ slotsProps.data.label }}</span>
          <button @click="getInfo(slotsProps.data)">获取Value</button>
        </template>
      </parent>
    </div>
  </div>
</template>

父组件:

<template>
  <div>
    <div v-for="item in options" :key="item.value">
      <child :info="item">
        <slot></slot>
      </child>
    </div>
  </div>
</template>

子组件:

<template>
  <div>
    <slot :data="info"></slot>
  </div>
</template>

我起初是这样写,结果报错了


意思就是在渲染slot时并没有获取到data对象,突然明白过来,在渲染到父组件时并没有给data赋值,所以demo.vue就开始报错了,那怎么样让父组件不渲染slot,在渲染子组件时才渲染呢,这里我是使用:render-label="$scopedSlots.default"向子组件传值的,而$scopedSlots.default就是作用域插槽了,那么这样在子组件中怎么调用这个方法,这里我是使用render函数实现的。才疏学浅没有想到其他的解决方法,希望各位大佬指教。

下面看一下我改正后的代码:

父组件:

<template>
  <div>
    <div v-for="item in options" :key="item.value">
      <child :render-label="$scopedSlots.default" :info="item">
      </child>
    </div>
  </div>
</template>

子组件:

render(h) {
    return(
      <span>
        {this.renderLabel({data: this.info})}
      </span>
    )
}

结果:


成功!撒花

后备内容

最后来补充一下后备内容,后备内容是指在父组件没有给slot传值时被渲染的内容,具体实现如下:

<!-- 默认插槽 -->
<slot>
    <span>后备内容</span>
</slot>
<!-- 具名插槽 -->
<slot name="head">
    <span>后备内容</span>
</slot>
<!-- 作用域插槽 -->
<slot :data="item">
    <span>后备内容</span>
</slot>

最后

这只是我的一些认识和理解,希望可以帮助更好的理解一下插槽,最后本文中如有不正确的地方,希望各位大佬斧正。

  • 9
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Vue3中,作用域插槽是一种通过插槽将父组件的数据传递给子组件的机制。与Vue2中的具名插槽不同,Vue3中的作用域插槽使用了新的语法来实现。 在Vue3中,你可以使用`<slot>`元素的`v-bind`指令来向插槽中传递数据。你可以在父组件中使用`v-bind`指令来绑定插槽中的数据,然后在子组件中通过`props`接收该数据。这样就能在子组件中使用父组件的数据了。 例如,假设我们有一个父组件`ParentComponent`和一个子组件`ChildComponent`,我们想要将父组件中的数据传递给子组件。我们可以在父组件的模板中使用作用域插槽来实现: ```html <ParentComponent> <template #default="data"> <ChildComponent :propName="data.property"></ChildComponent> </template> </ParentComponent> ``` 在上面的例子中,我们使用了`<template>`元素来定义作用域插槽,并且给插槽取了一个名字`default`。在子组件`ChildComponent`中,我们通过`props`接收了父组件传递过来的数据`propName`。 这样,子组件就可以访问到父组件的数据作用域中的`property`属性了。这是Vue3中作用域插槽的一种用法。 需要注意的是,作用域插槽只能从父组件向子组件传递数据,而无法访问子组件的数据。这是由于Vue模板中的表达式只能访问其定义时所处的作用域,与JavaScript的词法作用域规则一致。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [vue3 插槽详解](https://blog.csdn.net/qq_35191010/article/details/130153500)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值