1. 程式人生 > >深入理解vue slot插槽

深入理解vue slot插槽

單個插槽

只使用這個標籤的話,可以將父元件放在子元件的內容,放到想讓他顯示的地方

具名插槽

將放在子元件裡的不同html標籤放在不同的位置
父元件在要分發的標籤裡新增 slot=’name’ 屬性
子元件在對應分發的位置的slot標籤裡,新增name=’name’ 屬性,
然後就會將對應的標籤放在對應的位置了

這裡寫圖片描述

Vue slot 原理

<slot> 元素
Shadow DOM 使用 <slot> 元素將不同的 DOM 樹組合在一起。Slot 是元件內部的佔位符,使用者可以使用自己的標記來填充。

通過定義一個或多個 slot,您可將外部標記引入到元件的 shadow DOM 中進行渲染。 這相當於您在說“在此處渲染使用者的標記”。

注:Slot 是為網路元件建立“宣告性 API”的一種方法。它們混入到使用者的 DOM 中,幫助對整個元件進行渲染,從而將不同的 DOM 樹組合在一起。

如果 <slot> 引入了元素,則這些元素可“跨越” shadow DOM 的邊界。 這些元素稱為分散式節點。從概念上來看,分散式節點似乎有點奇怪。 Slot 實際上並不移動 DOM;它們在 shadow DOM 內部的其他位置進行渲染。

元件可在其 shadow DOM 中定義零個或多個 slot。Slot 可以為空,或者提供回退內容。 如果使用者不提供 light DOM 內容,slot 將對其備用內容進行渲染。

<!-- Default slot. If there's more than one default slot, the first is used. -->
<slot></slot> <slot>Fancy button</slot> <!-- default slot with fallback content --> <slot> <!-- default slot entire DOM tree as fallback --> <h2>Title</h2> <summary>Description text</summary> </slot>

您還可以建立已命名 slot。已命名 slot 是 shadow DOM 中使用者可通過名稱引用的特定槽。

例如 - <fancy-tabs> shadow DOM 中的已命名 slot:

#shadow-root
  <div id="tabs">
    <slot id="tabsSlot" name="title"></slot>
  </div>
  <div id="panels">
    <slot id="panelsSlot"></slot>
  </div>
元件使用者對 <fancy-tabs> 的宣告類似於:

<fancy-tabs>
  <button slot="title">Title</button>
  <button slot="title" selected>Title 2</button>
  <button slot="title">Title 3</button>
  <section>content panel 1</section>
  <section>content panel 2</section>
  <section>content panel 3</section>
</fancy-tabs>

<!-- Using <h2>'s and changing the ordering would also work! -->
<fancy-tabs>
  <h2 slot="title">Title</h2>
  <section>content panel 1</section>
  <h2 slot="title" selected>Title 2</h2>
  <section>content panel 2</section>
  <h2 slot="title">Title 3</h2>
  <section>content panel 3</section>
</fancy-tabs>

而且如果您很好奇,您會發現扁平樹看起來類似於:


<fancy-tabs>
  #shadow-root
    <div id="tabs">
      <slot id="tabsSlot" name="title">
        <button slot="title">Title</button>
        <button slot="title" selected>Title 2</button>
        <button slot="title">Title 3</button>
      </slot>
    </div>
    <div id="panels">
      <slot id="panelsSlot">
        <section>content panel 1</section>
        <section>content panel 2</section>
        <section>content panel 3</section>
      </slot>
    </div>
</fancy-tabs>

注意,我們的元件可處理不同的配置,但是扁平的 DOM 樹保持不變。 我們還可以從 <button> 切換到 <h2>。 編寫此元件的目的在於處理不同型別的子項 - 如同 <select> 一樣。

通過vue的渲染過程,可以看出slot渲染的原理
vm物件的$slots 會儲存元件的所有插槽的vdom, 主要通過解析children,如果子元件指定了插槽名,會將vdom push 到對應名稱的陣列中,否則會push到default的陣列中。

// named slots should only be respected if the vnode was rendered in the
// same context.
if ((child.context === context || child.fnContext === context) &&
  data && data.slot != null
) {
  const name = data.slot
  const slot = (slots[name] || (slots[name] = []))
  if (child.tag === 'template') {
    slot.push.apply(slot, child.children || [])
  } else {
    slot.push(child)
  }
} else {
  (slots.default || (slots.default = [])).push(child)
}

具體可以參看vue原始碼中的resolve-slots過程

那麼建立DOM的過程就可以通過名稱拿到具體元件:

// 單個插槽(匿名插槽)
this.$slots['default'];
// 具名插槽
this.$slots[slotName]