【Vue】說說你對slot的理解?slot使用場景有哪些?


 

 

一、slot是什么

在HTML中 slot 元素 ,作為 Web Components 技術套件的一部分,是Web組件內的一個占位符

該占位符可以在后期使用自己的標記語言填充,這樣就可以創建單獨的DOM樹,並將它與其它的組件組合在一起。

Vue中的概念也是如此

Slot 藝名插槽,花名“占坑”,我們可以理解為solt在組件模板中占好了位置,當使用該組件標簽時候,組件標簽里面的內容就會自動填坑(替換組件模板中slot位置),作為承載分發內容的出口

可以將其類比為插卡式的FC游戲機,游戲機暴露卡槽(插槽)讓用戶插入不同的游戲磁條(自定義內容)

 

二、使用場景

通過插槽讓用戶可以拓展組件,去更好地復用組件和對其做定制化處理

如果父組件在使用到一個復用組件的時候,獲取這個組件在不同的地方有少量的更改,如果去重寫組件是一件不明智的事情

通過slot插槽向組件內部指定位置傳遞內容,完成這個復用組件在不同場景的應用

比如布局組件、表格列、下拉選、彈框顯示內容等

 

三、分類

slot可以分來以下三種:

  • 默認插槽

  • 具名插槽

  • 作用域插槽

默認插槽

子組件用<slot>標簽來確定渲染的位置,標簽里面可以放DOM結構,當父組件使用的時候沒有往插槽傳入內容,標簽內DOM結構就會顯示在頁面

父組件在使用的時候,直接在子組件的標簽內寫入內容即可

子組件Child.vue

<template>
    <slot>
      <p>插槽后備的內容</p>
    </slot>
</template>

父組件

<Child>
  <div>默認插槽</div>  
</Child>

具名插槽

子組件用name屬性來表示插槽的名字,不傳為默認插槽

父組件中在使用時在默認插槽的基礎上加上slot屬性,值為子組件插槽name屬性值

子組件Child.vue

<template>
    <slot>插槽后備的內容</slot>
  <slot name="content">插槽后備的內容</slot>
</template>

父組件

<child>
    <template v-slot:default>具名插槽</template>
    <!-- 具名插槽⽤插槽名做參數 -->
    <template v-slot:content>內容...</template>
</child>

作用域插槽

子組件在作用域上綁定屬性來將子組件的信息傳給父組件使用,這些屬性會被掛在父組件v-slot接受的對象上

父組件中在使用時通過v-slot:(簡寫:#)獲取子組件的信息,在內容中使用

子組件Child.vue

<template> 
  <slot name="footer" testProps="子組件的值">
          <h3>沒傳footer插槽</h3>
    </slot>
</template>

父組件

<child> 
    <!-- 把v-slot的值指定為作⽤域上下⽂對象 -->
    <template v-slot:default="slotProps">
      來⾃⼦組件數據:{{slotProps.testProps}}
    </template>
    <template #default="slotProps">
      來⾃⼦組件數據:{{slotProps.testProps}}
    </template>
</child>

小結:

  • v-slot屬性只能在<template>上使用,但在只有默認插槽時可以在組件標簽上使用

  • 默認插槽名為default,可以省略default直接寫v-slot

  • 縮寫為#時不能不寫參數,寫成#default

  • 可以通過解構獲取v-slot={user},還可以重命名v-slot="{user: newName}"和定義默認值v-slot="{user = '默認值'}"

四、原理分析

slot本質上是返回VNode的函數,一般情況下,Vue中的組件要渲染到頁面上需要經過template -> render function -> VNode -> DOM 過程,這里看看slot如何實現:

編寫一個buttonCounter組件,使用匿名插槽

Vue.component('button-counter', {
  template: '<div> <slot>我是默認內容</slot></div>'
})

使用該組件

new Vue({
    el: '#app',
    template: '<button-counter><span>我是slot傳入內容</span></button-counter>',
    components:{buttonCounter}
})

獲取buttonCounter組件渲染函數

(function anonymous() {
with(this){return _c('div',[_t("default",[_v("我是默認內容")])],2)}
})

_v表示創建普通文本節點,_t表示渲染插槽的函數

渲染插槽函數renderSlot(做了簡化)

function renderSlot (
  name,
  fallback,
  props,
  bindObject
) {
  // 得到渲染插槽內容的函數    
  var scopedSlotFn = this.$scopedSlots[name];
  var nodes;
  // 如果存在插槽渲染函數,則執行插槽渲染函數,生成nodes節點返回
  // 否則使用默認值
  nodes = scopedSlotFn(props) || fallback;
  return nodes;
}

name屬性表示定義插槽的名字,默認值為defaultfallback表示子組件中的slot節點的默認值

關於this.$scopredSlots是什么,我們可以先看看vm.slot

function initRender (vm) {
  ...
  vm.$slots = resolveSlots(options._renderChildren, renderContext);
  ...
}

resolveSlots函數會對children節點做歸類和過濾處理,返回slots

function resolveSlots (
    children,
    context
  ) {
    if (!children || !children.length) {
      return {}
    }
    var slots = {};
    for (var i = 0, l = children.length; i < l; i++) {
      var child = children[i];
      var data = child.data;
      // remove slot attribute if the node is resolved as a Vue slot node
      if (data && data.attrs && data.attrs.slot) {
        delete data.attrs.slot;
      }
      // 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
      ) {
        // 如果slot存在(slot="header") 則拿對應的值作為key
        var name = data.slot;
        var slot = (slots[name] || (slots[name] = []));
        // 如果是tempalte元素 則把template的children添加進數組中,這也就是為什么你寫的template標簽並不會渲染成另一個標簽到頁面
        if (child.tag === 'template') {
          slot.push.apply(slot, child.children || []);
        } else {
          slot.push(child);
        }
      } else {
        // 如果沒有就默認是default
        (slots.default || (slots.default = [])).push(child);
      }
    }
    // ignore slots that contains only whitespace
    for (var name$1 in slots) {
      if (slots[name$1].every(isWhitespace)) {
        delete slots[name$1];
      }
    }
    return slots
}

_render渲染函數通過normalizeScopedSlots得到vm.$scopedSlots

vm.$scopedSlots = normalizeScopedSlots(
  _parentVnode.data.scopedSlots,
  vm.$slots,
  vm.$scopedSlots
);

作用域插槽中父組件能夠得到子組件的值是因為在renderSlot的時候執行會傳入props,也就是上述_t第三個參數,父組件則能夠得到子組件傳遞過來的值

轉自:https://blog.csdn.net/weixin_44475093/article/details/111244171


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM