复习-vue的slot插槽

86 阅读7分钟

看别人代码突然有点忘了插槽,复习下。

定义

Vue 实现了一套内容分发的 API,将 <slot> 元素作为承载分发内容的出口。

参照了其他人的文章,有句话很受用:父组件可以堂而皇之地插入内容(不局限于变量或者函数)到子组件中。 插槽是对子组件的扩展,可以向子组件指定的位置传递内容。 www.jianshu.com/p/9a7f8ab07…

亦或者可以说是子组件挖好了坑,父组件可以在对应的坑里种东西。

具名插槽

上例子-基础用法:

子组件内容-注册组件,先挖好坑

    Vue.component('my-component', {
        data() {
            return {
               message: '我是子组件的信息', //先不管这里
               text: '我是子组件的文本'//先不管这里
            }
        },
        template: `<div class="child">
           <slot name="header"></slot> 
           </div>`

一般地,在一个调用了子组件的父组件中,给子组件的标签中间直接加内容,是不会成功渲染的。如下:

<!-- 挂载的子组件-->
<my-component>
 xxxxxxxxx内容
</my-component>

这里页面不会渲染任何东西(即便有slot,但也没定义默认内容)

但是我们可以使用插槽传递内容

父组件内容-注册实例,给坑填东西

   new Vue({
        el: '#app',
        data() {
            return {
                message: '我是父组件的信息' //先不管这里
            }
        }
    })

html:

   <div id="app">
      <!-- 子组件内容 -->
      <my-component>
          <template v-slot:header>
            <h3>我是子组件h3标题</h3>
          </template>
      </my-component>
   
      <!-- 父组件内容 -->
      <p>我是正文</p>
   </div>

效果如下:

Snipaste_2021-10-06_23-39-04.png

这里使用的是具名插槽,定义了slotname"header"

然后在子组件挂载处按照预设的name传递内容,2.6以后的写法是使用v-slot:header指向传递的具名插槽。

其中类似v-bindv-on存在缩写:# <=> v-slot

后备内容(默认内容)

对于一个子组件的插槽,可以预先定义其中的内容,如果调用该子组件时不传递内容,就会显示预先定义的内容,如下:

   <!-- 子组件预先定义内容 -->
   Vue.component('my-component', {
        template: `<div class="child">
                 <slot name="header">
                   姜维大战邓艾
                 </slot>
               </div>`
        })
   <!-- 父组件调用不传递内容 -->
   <my-component>
      <template #header>
      </template>
   </my-component>

渲染效果如下:

默认slot内容.png

并且对于一个没有定义name的 slot,叫做默认插槽或匿名插槽, 其默认的name其实是"default",对于未指定nametemplate其内容会在默认插槽中承接。

效果如下:

    Vue.component('my-component', {
        template: `<div class="child">    
               <slot name="header">
                姜维大战邓艾
               </slot>
                <slot>
                </slot>
                  </div>`
    })
  <my-component>
      <template>
         <div>
            钟会在边上看
         </div>
      </template>
   </my-component>

渲染效果如下:

默认插槽.png

注意:渲染内容取决于父组件的传递,但是渲染顺序取决于子组件slot的排列

举例如下:

父组件footer和header反向排列

 <my-component>
        <template #footer>
            <div>
                我是footer插槽里的东西
            </div> 
        </template>
       <template #header>
            <div>
                我是header插槽里的东西
            </div>
       </template>

子组件排列从上至下是header和footer

   Vue.component('my-component', {
        data() {
            return {
               message: '我是子组件的信息',
               text: '我是子组件的文本'
            }
        },
        template: `<div class="child">    
                <slot name="header">
                   header
                </slot>
                <slot name="footer">
                   footer
                </slot>
               </div>`
    })

渲染效果如下:

取决于子组件排列顺序.png

注意: v-slot 只能添加在 <template> 上 (只有一种例外情况)

这里的例外情况就是指独占作用域插槽的缩写写法,可以省略template标签直接把v-slot加在子组件上,用子组件作为模板。 但是注意该缩写不能和具名插槽混用,会导致作用域不明确报错:

   <my-component #="{ text }" >
        <div>
            {{` 我是默认插槽里的text-${text}`}}
         </div>
           <template #header="{ message }">
            <div>
                {{` 我是header插槽里的message-${message}`}}
             </div> 
            
           </template>
    </my-component>       
  Vue.component('my-component', {
        data() {
            return {
               message: '我是子组件的信息',
               text: '我是子组件的文本'
            }
        },
        template: `<div class="child">    
                <slot name="header" :message="message" >
                    header
                </slot>
                <slot :text="text">
                </slot>
                  </div>`
    })

作用域不明确报错.png

具体情况可以查看链接,不再赘述举例

在上文的例子中显然可以看到,slot和props和区别在于slot不光可以传递属性和方法,还可以传递标签和组件!

作用域插槽

对子组件的插槽绑定子组件的属性,父组件中用v-slot设置一个值来定义我们提供插槽的名字,使得父组件可以访问到对应的子组件的属性

在子组件绑定属性

Vue.component('my-component', {
        data() {
            return {
               joker: '张嘉文',
               words: '我是全世界最强的男人白狐子阿!'
            }
        },
        template: `<div class="child">    
                    <slot name="header" :joker="joker" :words="words">
                    </slot>
                  </div>`
    })

在父组件用slotScope接收,也可以使用别的名字,自己喜欢就好

<div id="app">
        <!-- 子组件内容 -->
      <my-component>
           <template v-slot:header="slotScope">
            <div>
                {{` 我是header插槽里的-${slotScope.joker}`}}
             </div> 
             <div>
                {{slotScope.words}}
             </div>
           </template>
      </my-component>
      <!-- 父组件内容 -->
      <p>我是正文</p>
    </div>

渲染结果如下:

作用域插槽.png

还可以手动转换属性名,支持批量

<div id="app">
        <!-- 子组件内容 -->
      <my-component>
           <template v-slot:header="{joker: person, words: ws}">
            <div>
                {{` 我是header插槽里的-${person}`}}
             </div> 
             <div>
                {{ws}}
             </div>
           </template>
      </my-component>
      <!-- 父组件内容 -->
      <p>我是正文</p>
    </div>

也可以顺利渲染,效果同上

解构

官方文档的解析:作用域插槽的内部工作原理是将你的插槽内容包裹在一个拥有单个参数的函数里:

function (slotProps) { // 插槽内容 }

这意味着v-slot 的值实际上可以是任何能够作为函数定义中的参数的 JavaScript 表达式。所以在支持的环境下 (单文件组件现代浏览器),你也可以使用 ES2015 解构来传入具体的插槽 prop,如下:

    Vue.component('my-component', {
        data() {
            return {
               message: '我是子组件的信息',
               text: '我是子组件的文本'
            }
        },
        template: `<div class="child">    
                    <slot name="header" :message="message" >
                        header
                    </slot>
                    <slot name="footer" :text="text">
                        footer
                    </slot>
                  </div>`
    })
<div id="app">
        <!-- 子组件内容 -->
      <my-component>
           <template #header="{ message }">
            <div>
                {{` 我是header插槽里的message-${message}`}}
             </div> 
           </template>
           <template #footer=" { text } ">
            <div>
                {{` 我是footer插槽里的text-${text}`}}
             </div>
           </template>
      </my-component>
      <!-- 父组件内容 -->
      <p>我是正文</p>
    </div>

渲染如下:

作用域插槽解构.png

动态插槽名还没使用过,用的时候再看一眼就行了~

尾巴

    加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油加油