从头再学Vue之slot插槽

it2023-12-04  60

介绍

在Vue中提供了一个内置组件:「slot」,官方称之为「插槽」。其作用主要是为了做内容分发。内容分发这个词理解起来可能不太直观,如果学习过Angular,就可以将它理解为Angular中的ng-content,ng-content的解释是「内容投影」,就将内容投影到组件的ng-content中。slot和它的作用是一样的。

如何使用

在官方中给出了通过插槽分发内容的例子。首先定义一个组件,在组件模板中使用「slot」标记插槽在模板中的位置:

Vue.component('alert-box', {   template: `     <div class="demo-alert-box">       <strong>Error!</strong>       <slot></slot>     </div>   ` })

使用自定义组件:

<alert-box>   Something bad happened. </alert-box>

其中, “Something bad happened.”作为内容被分发到自定义组件中的「slot」处,效果如下:

<div class="demo-alert-box">   <strong>Error!</strong>   Something bad happened. </div>

这样我们就通过使用「slot」完成了内容分发。

应用场景

slot的应用场景有很多,如通过slot实现统一的布局、结构、效果等。在 Element UI中就有许多使用slot的组件。如表单中的FormItem:

<template>   <div class="el-form-item" :class="[{       'el-form-item--feedback': elForm && elForm.statusIcon,       'is-error': validateState === 'error',       'is-validating': validateState === 'validating',       'is-success': validateState === 'success',       'is-required': isRequired || required,       'is-no-asterisk': elForm && elForm.hideRequiredAsterisk     },     sizeClass ? 'el-form-item--' + sizeClass : ''   ]">     <label-wrap       :is-auto-width="labelStyle && labelStyle.width === 'auto'"       :update-all="form.labelWidth === 'auto'">       <label :for="labelFor" class="el-form-item__label" :style="labelStyle" v-if="label || $slots.label">         <slot name="label">{{label + form.labelSuffix}}</slot>       </label>     </label-wrap>     <div class="el-form-item__content" :style="contentStyle">       <slot></slot>       <transition name="el-zoom-in-top">         <slot           v-if="validateState === 'error' && showMessage && form.showMessage"           name="error"           :error="validateMessage">           <div             class="el-form-item__error"             :class="{               'el-form-item__error--inline': typeof inlineMessage === 'boolean'                 ? inlineMessage                 : (elForm && elForm.inlineMessage || false)             }"           >             {{validateMessage}}           </div>         </slot>       </transition>     </div>   </div> </template>

在FormItem组件中,使用到了3个slot,从上而下它们的作用分别是:

label内容分发

默认内容分发

错误内容分发

进阶

命名

在FormItem的例子中,发现在slot中使用到了name属性。name是用来命名插槽的。在模板中通过name来区分多个插槽。

<div class="container">   <header>     <!-- 我们希望把页头放这里 -->   </header>   <main>     <!-- 我们希望把主要内容放这里 -->   </main>   <footer>     <!-- 我们希望把页脚放这里 -->   </footer> </div>

要实现上面的效果,就可以使用具名插槽:

<div class="container">   <header>     <slot name="header"></slot>   </header>   <main>     <slot></slot>   </main>   <footer>     <slot name="footer"></slot>   </footer> </div>

不带name属性的插槽,会带有隐含的名字“default”。使用如下:

<base-layout>   <template v-slot:header>     <h1>Here might be a page title</h1>   </template>   <p>A paragraph for the main content.</p>   <p>And another one.</p>   <template v-slot:footer>     <p>Here's some contact info</p>   </template> </base-layout>

渲染效果如下:

<div class="container">   <header>     <h1>Here might be a page title</h1>   </header>   <main>     <p>A paragraph for the main content.</p>     <p>And another one.</p>   </main>   <footer>     <p>Here's some contact info</p>   </footer> </div>

缩写

具名插槽的使用时的v-slot可以缩写为:

<base-layout>   <template #header>     <h1>Here might be a page title</h1>   </template>   <p>A paragraph for the main content.</p>   <p>And another one.</p>   <template #footer>     <p>Here's some contact info</p>   </template> </base-layout>

插槽 prop

在实际开发过程中,很多时候需要将组件内的数据通过slot的方式提供给外部使用,此时,就是通过slot属性的方式将数据传递到组件外:current-user

<span>   <slot v-bind:user="user">     {{ user.lastName }}   </slot> </span>

使用时:

<current-user>   <template v-slot:default="slotProps">     {{ slotProps.user.firstName }}   </template> </current-user>

只有默认插槽时,我们可以对上面的使用方式进行简化:

<current-user v-slot:default="slotProps">   {{ slotProps.user.firstName }} </current-user>

不带参数的slot默认为default,所以还可以简化为:

<current-user v-slot="slotProps">   {{ slotProps.user.firstName }} </current-user>

解构插槽prop

在ES中,提供了对象解构功能,所以我们也可以使用解构的方式来处理slot:

<current-user v-slot="{ user }">   {{ user.firstName }} </current-user>

此处可以理解为:

let { user } = slotProps

通过解构的方式,将slotProps的user属性提取出来。

总结

「slot」的内容分发功能,可以帮助开发者更加方便的实现布局结构组件化的复用。同样也是开发者进阶的一个标志。

最新回复(0)