插槽<slot>

一、插槽与透传\props引用父DOM的区别

1、透传\props引用父DOM

  • 子组件不能完整引用DOM,只能引用DOM属性。
  • 引用方法是: 将DOM变量赋给父标签属性。在子组件中引用DOM变量的属性。
    如下:是使用DOM的outerHTML属性生成的类似标签,这个标签只重现了outerHTM能包含的原DOM标签上的属性,并不能重现原DOM标签上的事件等其它内容。
    父组件:

    <template>
    <button ref="wow" @click="ed" >草</button> 
    <HelloWor :oj="wow"  />
    </template>
    <script setup>
    import HelloWor from './Hello.vue'
    import {ref} from 'vue'
    function ed(){console.log('能成功打印吗')}
    const wow=ref({})//我是DOM
    </script>

    子组件:

    <template>
     <div >{{'oj DOM:'+$attrs.oj}}</div>
     <div v-html="$attrs.oj.outerHTML"></div>
    </template>
    <script setup></script>

2、插槽引用父DOM

  • 子组件能完整引用DOM,但不能单独引用DOM属性。
  • 引用方法是: 借助<template>标签,要传的DOM对象放置在<template>标签里。在子组件中用<slot>接收<template>标签,连同接收<template>里的DOM对象。
    如下:接收<template>,连同接收<template>里的DOM对象。子组件接收的DOM对象是完整的,属性和事件、指令都是完整复刻。
    父组件:

    <template>
    <HelloWor :oj="wow">
     <template v-slot:dg><button ref="wow" @click="ed" >草</button></template>
    </HelloWor>
    </template>
    <script setup>
    import HelloWor from './Hello.vue'
    import {ref}from 'vue'
    const wow=ref({})
    function ed(){console.log('能成功打印吗')}
    </script>

    子组件:

    <template>
      <slot name="dg"></slot>
     {{$attrs.oj.outerHTML}}
    </template>

    v-slot:dg给<template>具名,<slot name="dg">接收此具名<template>

3、插槽与透传\props的区别

a、DOM本体
透传\props不需在父标签下放置DOM本体,只需在父标签的属性上赋值DOM变量;
插槽要把DOM本体放在父标签下的<template>标签内。
b、引用内容
透传\props只能引用DOM对象的属性,不能引用完整的DOM对象;
插槽是接收完整的DOM对象,并不能单独引用DOM对象的属性。

二、<template>标签

1、<template>标签规范

a、必须顶级

<template>标签在父标签下必须是顶级标签,不可放在其它标签内。

b、隐式<template>

系统会强行把父标签内所有非<template>标签打包进一个隐式的<template>标签内,并自动为其添加v-slot:指令绑定default具名。

c、<template>内容

<template>身处父组件内,可以直接访问父组件的所有数据(变量与函数),所以不需额外向子组件传递参数。
<template>的内容可以是任意合法的模板内容,不局限于文本。例如我们可以传入多个标签,甚至是组件。
<template>标签上的属性不会生效,如下style="color:red"并不会使字体变红

<template #rt style="color:red">
    我会变红吗?
</template>

2、v-slot指令规范

v-slot:具名="子参合集对象" v-slot:指令可简写成#

a、具名

props是通过父标签的属性名来引用DOM的。插槽则是通过<template>标签的具名来引用DOM。

  • 注意具名是指令参数,应以参数形式书写,指令值是子参合集对象。即v-slot:具名=“子参合集对象”。通常不需要子参,因此指令多是无值形式出现,如下:
    <template v-slot:具名></template>
    简写<template #具名></template>
  • 特殊具名default
    隐式<template>会自动绑定default具名。
    当父标签内没有隐式<template>时,可以给非隐式的<template>标签手动绑定default具名。
    手动具名default的简写形式v-slot:或#
    default具名的<template>在子组件里引用时有略写形式(见后文“<slot>标签规范--default略写”)。
  • <template>子标签不可再具名
    <template>子标签不可再具名。父标签下的非<template>标签已是隐式<template>标签的子标签,不可再具名,否则系统会报错。
  • 具名唯一性
    具名唯一,不可与其它<template>标签的具名相同。父标签下的非<template>标签会生成具名为“default”的隐式<template>,其它<template>标签不可再具名为“default”,这会违反具名唯一性。
  • 动态具名
    具名可以是动态值,具名​变量要放在[]中括号里,如下:<template #[de]>我具名可变</template>
b、子参合集对象
  • v-slot指令值是子参合集对象
    v-slot指令值是子参合集对象,子组件传递过来的参数是此对象的子属性。v-slot指令无值则无法接收子传过来参数。
  • 子参合集对象解构
    可以直接在v-slot指令值上原地解构,获得子参合集对象内的一个子参,如:<template #="{bb}">
  • 子参作用域
    子参只能在本v-slot指令标签及下级标签上使用,不能在本v-slot指令标签外使用。
  • 事件、自定义事件子参的书写
    事件子参要注意事件名前要加on,首写字母要大写。
  • 隐式具名插槽的弊端
    隐式具名插槽无法给v-slot指令赋值,所以尽量不要使用隐式具名插槽,而是把非<template>标签放到<template>里并手动具名default
    接收子参如下:
    父组件

    <template>
    <HelloWor :oj="wow">
     <template  #de="zican"><button ref="wow" @click="ed" >{{zican.oo}}</button></template>
    </HelloWor>
    </template>
    <script setup>
    import HelloWor from './Hello.vue'
    import {ref}from 'vue'
    const wow=ref({})
    function ed(){console.log('能成功打印吗')}
    </script>

    子组件

    <template>
      <slot name="de" oo="广州市"></slot>
     {{$attrs.oj.outerHTML}}
    </template>

三、<slot>规范

1、name配对

子组件通过<slot>标签的name属性值与父标签内的<template>标签的v-slot:指令具名配对接收<template>

2、<slot>name同名

子组件多个<slot>标签可以引入同一个<template>,name值为同一<template>具名

3、default略写

name="default"时,可以略写,即<slot name="default"></slot>可以略写成<slot></slot>
略写的<slot>标签会与父标签内具名为default的<template>标签配对。

4、<template>缺省

可设置无<template>配对时<slot>的缺省内容,缺省内容书写在<slot>标签下。
缺省内容只在没有配对到<template>时才会渲显示。

5、<slot>传参

<slot>标签内的属性与事件并不会作用引入的<template>及缺省<template>时的内容​。只单纯的传递参数的作用。
可传递的内容:子组件的所有数据(变量、函数)及<slot>标签事件。
实例如下:
父组件

<template>
  <HelloWor >
  <template v-slot:rtr="hhh" >
    <button @click="hhh.onClick" >测试事件子传父</button>
  </template>
  <template v-slot:default="tt">
   {{ tt.oo}}
  </template>
  <template #rt style="color:red">
    我会变红吗?
  </template>
</HelloWor>
</template>
<script setup>
import HelloWor from './Hello.vue'
</script>

子组件

<template>
  <slot oo="我引入具名default的&lt;lttemplate &gt;"></slot><br>
  <slot oo="我也是引入具名default的&lt;lttemplate &gt;"></slot><br>
  <slot name="rt"></slot><br>
  <slot name="rtr" @click="()=>{console.log('事件子传父')}"></slot><br>
  <slot name="rrrtr"><button>我是&lt;template &gt;缺省内容</button></slot>
</template>
<script></script>

四、无逻辑父组件与无渲染子组件

插槽子传父可以导致父组件不需要逻辑部来处理数据,只专注于显示DOM,而子组件不需要模板部来显示DOM,只专注于处理数据。这种父组件可称为无逻辑组件,子组件可称为无渲染组件。
虽然这个模式很简便,但大部分能用无渲染组件实现的功能都可以通过组合式 API 以另一种更高效的方式实现,并且还不会带来额外组件嵌套的开销。


百分之一百零八
15 声望3 粉丝