Vuetify中使用v-slot插槽

简介

Vue自2.6.0版本开始为具名插槽和做用域插槽引入了一个新的统一的语法 (即 v-slot 指令)。它取代了 slot 和 slot-scope 这两个目前已被废弃但未被移除且仍在文档中的 attribute。
简言之,插槽技术的主要引入目的是承担“占位符”(placeholder)的做用。相信许多朋友都熟悉“占位符”这个概念,即在构造UI或实现某项功能时而还未给出具体实现方案时先使用一个最简内容来代替,而不至于编译等过程当中出现语法错误或者尽可能避免尴尬。请参考官方提供的以下简例:html

有时为一个插槽设置具体的后备 (也就是默认的) 内容是颇有用的,它只会在没有提供内容的时候被渲染。例如在一个 <submit-button> 组件中:vue

<button type="submit">
  <slot></slot>
</button>

咱们可能但愿这个 <button> 内绝大多数状况下都渲染文本“Submit”。为了将“Submit”做为后备内容,咱们能够将它放在 <slot> 标签内:浏览器

<button type="submit">
  <slot>Submit</slot>
</button>

如今当我在一个父级组件中使用 <submit-button> 而且不提供任何插槽内容时:babel

<submit-button></submit-button>

后备内容“Submit”将会被渲染:app

<button type="submit">
  Submit
</button>

可是若是咱们提供内容:ide

<submit-button>
  Save
</submit-button>

则这个提供的内容将会被渲染从而取代后备内容:函数

<button type="submit">
  Save
</button>

具名插槽

在向具名插槽提供内容的时候,咱们能够在一个 <template> 元素上使用 v-slot 指令,并以 v-slot 的参数的形式提供其名称:布局

<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>

上面冒号后面的header、footer等就至关于2.6版本之后的插槽的name属性。ui

如今 <template> 元素中的全部内容都将会被传入相应的插槽。任何没有被包裹在带有 v-slot 的 <template> 中的内容都会被视为默认插槽的内容。this

然而,若是你但愿更明确一些,仍然能够在一个 <template> 中包裹默认插槽的内容:

<base-layout>
  <template v-slot:header>
    <h1>Here might be a page title</h1>
  </template>

  <template v-slot:default>
    <p>A paragraph for the main content.</p>
    <p>And another one.</p>
  </template>

  <template v-slot:footer>
    <p>Here's some contact info</p>
  </template>
</base-layout>

【注意】 v-slot 只能添加在 <template> 上 (只有一种例外状况),这一点和已经废弃的 slot attribute 不一样。

做为一条规则,请记住:

父级模板里的全部内容都是在父级做用域中编译的;子模板里的全部内容都是在子做用域中编译的。

做用域插槽

有时让插槽内容可以访问子组件中才有的数据是颇有用的。

绑定在 <slot> 元素上的 attribute 被称为【插槽 prop】。如今在父级做用域中,咱们能够使用【带值的 v-slot 】来定义咱们提供的插槽 prop 的名字:

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

在这个例子中,咱们选择将包含全部插槽 prop 的对象命名为 slotProps,但你也能够使用任意你喜欢的名字。

独占默认插槽的缩写语法

在上述状况下,当被提供的内容只有默认插槽时,组件的标签才能够被看成插槽的模板来使用。这样咱们就能够把 v-slot 直接用在组件上:

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

这种写法还能够更简单。就像假定未指明的内容对应默认插槽同样,不带参数的 v-slot 被假定对应默认插槽:

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

注意默认插槽的缩写语法不能和具名插槽混用,由于它会致使做用域不明确。

只要出现多个插槽,请始终为全部的插槽使用完整的基于 <template> 的语法:

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

  <template v-slot:other="otherSlotProps">
    ...
  </template>
</current-user>

解构插槽 Prop

做用域插槽的内部工做原理是将插槽内容包裹在一个拥有单个参数的函数里:

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

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

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

这样能够使模板更简洁,尤为是在该插槽提供了多个 prop 的时候。它一样开启了 prop 重命名等其它可能,例如将 user 重命名为 person:

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

你甚至能够定义后备内容,用于插槽 prop 是 undefined 的情形:

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

插槽 prop 容许咱们将插槽转换为可复用的模板,这些模板能够基于输入的 prop 渲染出不一样的内容。这在设计封装数据逻辑同时容许父级组件自定义部分布局的可复用组件时是最有用的。

接下来的内容,咱们结合Vuetify UI库的经常使用组件v-menu的使用来探讨如何在Vuetify环境中使用v-slot。

Vuetify中使用v-slot插槽

请看代码(较复杂的例子,基本举例见后面两个):

<template>
  <div class="text-center">
    <v-menu>
      <template v-slot:activator="{ on: menu, attrs }">
        <v-tooltip bottom>
          <template v-slot:activator="{ on: tooltip }">
            <v-btn
              color="primary"
              dark
              v-bind="attrs"
              v-on="{ ...tooltip, ...menu }"
            >
              Dropdown w/ Tooltip
            </v-btn>
          </template>
          <span>Im A ToolTip</span>
        </v-tooltip>
      </template>
      <v-list>
        <v-list-item
          v-for="(item, index) in items"
          :key="index"
        >
          <v-list-item-title>{{ item.title }}</v-list-item-title>
        </v-list-item>
      </v-list>
    </v-menu>
  </div>
</template>

现不做具体解释,先来看一个码农岛上的问答解释,尽管是英语,可是相应各位理解起来没有问题,因此在此并不翻译:

问题:

Looking at the Vuetify example code for v-toolbar, what is the purpose of v-slot:activator="{ on }"? For example:

<template v-slot:activator="{ on }">
  <v-toolbar-title v-on="on">
    <span>All</span>
    <v-icon dark>arrow_drop_down</v-icon>
  </v-toolbar-title>
</template>

回答

You're likely referring to this example:

<v-toolbar color="grey darken-1" dark>
  <v-menu :nudge-width="100">
    <template v-slot:activator="{ on }">
      <v-toolbar-title v-on="on">
        <span>All</span>
        <v-icon dark>arrow_drop_down</v-icon>
      </v-toolbar-title>
    </template>

    ...
  </v-menu>
</v-toolbar>

The following line declares a scoped slot named activator, and it is provided a scope object (from VMenu), which contains a property named on:

&lt;template v-slot:activator="{ on }"&gt;
This uses destructuring syntax on the scope object, which IE does not support.

For IE, you'd have to dereference on from the scope object itself:

<template v-slot:activator="scope">
  <v-toolbar-title v-on="scope.on">

But the ideal solution IMO is to use a Vue CLI generated project, which includes a Babel preset (@vue/babel-preset-app) to automatically include the transforms/polyfills needed for the target browsers. In this case, babel-plugin-transform-es2015-destructuring would be automatically applied during the build.

Details on the activator slot
VMenu allows users to specify a slotted template named activator, containing component(s) that activate/open the menu upon certain events (e.g., click). VMenu provides listeners for those events via an object, passed to the activator slot:

<v-menu>
  <template v-slot:activator="scopeDataFromVMenu">
    <!-- slot content goes here -->
  </template>
</v-menu>

The slot content can access VMenu's event listeners like this:

<v-menu>
  <template v-slot:activator="scopeDataFromVMenu">
    <button v-on="scopeDataFromVMenu.on">Click</button>
  </template>
</v-menu>

For improved readability, the scoped data can also be destructured in the template:

<!-- equivalent to above -->
<v-menu>
  <template v-slot:activator="{ on }">
    <button v-on="on">Click</button>
  </template>
</v-menu>

The listeners from the scope object are passed to the <button> with v-on's object syntax, which binds one or more event/listener pairs to the element. For this value of on:

{
click: activatorClickHandler // activatorClickHandler is an internal VMenu mixin
}
...the button's click handler is bound to a VMenu method.

概括一下,Vuetify中的几个经常使用组件(v-menu、v-tooltip和v-dialog)中使用v-slot的基础基于Vue中的具名做用域插槽技术,而且使用了ES2015(即ES6)语法中的“析构”(destructing)。

以v-menu典型应用为例,经过具名做用域插槽它把本身的事件集和属性集传递给<template>内部子组件,并根据须要经过v-bind和v-on语法把子组件的特定属性和事件映射到外层父组件对应的属性集和事件集上。

v-slot结合v-tooltip的应用

<v-tooltip bottom>
      <template v-slot:activator="{ on, attrs }">
        <v-btn
          color="primary"
          dark
          v-bind="attrs"
          v-on="on"
        >
          Button
        </v-btn>
      </template>
      <span>Tooltip</span>
    </v-tooltip>

v-slot结合v-dialog的应用

<v-dialog
      v-model="dialog"
      width="500"
    >
      <template v-slot:activator="{ on, attrs }">
        <v-btn
          color="red lighten-2"
          dark
          v-bind="attrs"
          v-on="on"
        >
          Click Me
        </v-btn>
      </template>

      <v-card>
        <v-card-title class="headline grey lighten-2">
          Privacy Policy
        </v-card-title>

        <v-card-text>
          Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
        </v-card-text>

        <v-divider></v-divider>

        <v-card-actions>
          <v-spacer></v-spacer>
          <v-btn
            color="primary"
            text
            @click="dialog = false"
          >
            I accept
          </v-btn>
        </v-card-actions>
      </v-card>
    </v-dialog>

重要参考

https://blog.csdn.net/weixin_44710964/article/details/107428727
https://cn.vuejs.org/v2/guide/components-slots.html
https://vuetifyjs.com/en/components/menus/#activator-and-tooltip
https://www.manongdao.com/article-1850182.html

相关文章
相关标签/搜索