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

相關文章
相關標籤/搜索