溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Vue.slot原理及slot是如何實現(xiàn)的

發(fā)布時間:2023-03-15 10:40:25 來源:億速云 閱讀:115 作者:iii 欄目:編程語言

本篇內(nèi)容介紹了“Vue.slot原理及slot是如何實現(xiàn)的”的有關(guān)知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠?qū)W有所成!

一、回顧 slot 用法

1. 默認插槽

<!-- 子組件 -->
<template>
  <div class="wrapper">
    <!-- 默認插槽 -->
    <div class="main">
      <slot></slot>
    </div>
</template>

<!-- 父組件 -->
<my-slot>
  <template>
    <h2>默認插槽</h2>
  </template>
</my-slot>

頁面展示效果如圖:

Vue.slot原理及slot是如何實現(xiàn)的

2. 具名插槽

接著上述的案例,添加具名插槽 header ,代碼如下:

<!-- 子組件 -->
<template>
  <div class="wrapper">
    <!-- header 具名插槽 -->
    <header class="header">
      <slot name="header"></slot>
    </header>
    <!-- 默認插槽 -->
    <div class="main">
      <slot></slot>
    </div>
</template>

<!-- 父組件 -->
<my-slot>
  <template v-slot:header>
    <h2>header 具名插槽</h2>
  </template>
  <template>
    <h2>默認插槽</h2>
  </template>
</my-slot>

如上代碼塊可以發(fā)現(xiàn):

  • 子組件中的 slot標簽 帶上了一個名為 name 的屬性,值為 header

  • 父組件中的 template標簽 帶上了 v-slot 的屬性,值為 header

頁面展示效果如圖:

Vue.slot原理及slot是如何實現(xiàn)的

3. 作用域插槽(slot-scope)

再接著上述案例,添加作用域插槽 footer ,代碼如下

<!-- 子組件 -->
<template>
  <div class="wrapper">
    <!-- header 具名插槽 -->
    <header class="header">
      <slot name="header"></slot>
    </header>
    <!-- 默認插槽 -->
    <div class="main">
      <slot></slot>
    </div>
    <!-- footer 具名 + 作用域插槽 -->
    <footer class="footer">
      <slot name="footer" :footerInfo="footerInfo"></slot>
    </footer>
  </div>
</template>
<script>
export default {
  name: "mySlot",
  data () {
    return {
      footerInfo: {
        text: '這是 子組件 footer插槽 的作用域數(shù)據(jù)'
      }
    }
  }
}
</script>

<!-- 父組件 -->
<my-slot>
  <template v-slot:header>
    <h2>header 具名插槽</h2>
  </template>
  <template>
    <h2>默認插槽</h2>
  </template>
  <template v-slot:footer="slotProps">
    <h2>footer 具名 + 作用域插槽</h2>
    <p>{{ slotProps.footerInfo.text }}</p>
  </template>
</my-slot>

如上代碼塊可以發(fā)現(xiàn):

  • 子組件中的 slot標簽 除了有 name=footer 的屬性,還有一個:footerInfo="footerInfo" 的屬性(作用就是傳遞子組件數(shù)據(jù))

  • 父組件中的 template標簽 不僅有 v-slot:footer ,并且還有一個賦值操作 ="slotProps",在模版的雙括號語法中,直接通過 slotProps 訪問到 子組件的 footerInfo

頁面展示效果如圖:

Vue.slot原理及slot是如何實現(xiàn)的

好了,簡單回顧完用法后,筆者在這里先提三個問題:

  1. 普通插槽、 作用域插槽 的 vNode 是在哪個環(huán)節(jié)生成的,render 父組件時還是子組件時?

  2. 作用域插槽 為什么能在父組件訪問到子組件的數(shù)據(jù)?

  3. 普通插槽 跟 作用域插槽 在實現(xiàn)上有區(qū)別嗎?

我們帶著疑問接著往下看!

二、不同slot的編譯區(qū)別

我們根據(jù)上述最終的案例代碼,執(zhí)行一下打包命令,看看 Vue 在編譯模板的時候,是怎么處理我們的 slot 的!事不宜遲,趕緊 build 一哈~(偷偷告訴大?,Vue 處理 作用域插槽普通插槽 的差異就是從編譯開始的,也就是 render函數(shù) 會有所不同)

這里筆者順便使用 v2.5 的具名插槽寫法給大?參照一下(對具名插槽header做改寫,使用 slot="header" 的寫法),大家可以看下 v2.6、v2.5 具名插槽的 寫法、實現(xiàn) 上的區(qū)別~反正也不難,也就順便帶出來看看了

Vue.slot原理及slot是如何實現(xiàn)的

上圖左邊是 v2.6 、右邊是 v2.5 的,這里,我們集中關(guān)注:

  • scopedSlots 屬性。使用作用域插槽的 footer 的 render函數(shù) 是放在 scopedSlots 里的,并且 函數(shù)中 還有接收一個參數(shù)

  • my-slotchildren??梢园l(fā)現(xiàn),默認插槽的 render函數(shù) 一直都是作為當前組件的childre節(jié)點,放在 當前 組件render函數(shù) 的第三個參數(shù)

  • 關(guān)注 header 兩種具名插槽寫法的區(qū)別。

    • v2.6 中,我們使用了具名插槽,但是又未使用 作用域插槽的 header 被放在了 scopedSlots,但是函數(shù)的參數(shù)為空,這點跟作用域插槽有區(qū)別。

    • v2.5 中, 具名插槽header 僅僅作為 my-slot組件 的children節(jié)點,并且其render函數(shù)的第二個參數(shù)中有一個 slot 的屬性。

其實根據(jù)上述編譯后的結(jié)果,我們不妨這樣猜測

  • 默認插槽直接在父組件的 render 階段生成 vNode。

    • 子組件 render 時,可能通過某種手段取得已經(jīng)生成好的 插槽vNode 用作自己的 slot 節(jié)點。

    • 因為觀察上述默認插槽的render函數(shù):e("h2", [t._v("默認插槽")]),直接就是 my-slot 組件的childre節(jié)點(位于 my-slot 組件的第三個參數(shù))。

  • 作用域插槽是在子組件 render 階段生成 vNode。

    • 因為我們觀察作用域插槽 footer 的編譯后結(jié)果,其不作為 my-slot 組件的 children,而是被放在了 my-slot 組件的 第二個參數(shù) data 中的一個 scopedSlots屬性里。

    • 并且,作用域插槽的 render 函數(shù) 外層的 funciton 接收了一個參數(shù)。如果在執(zhí)行子組件 render 的時候調(diào)用,那完全可以拿到子組件的數(shù)據(jù)。

這里放出具體的 作用域插槽 打包后代碼,大家一看就很清晰了:

{
  scopedSlots: t._u([
    {
      key: "footer", 
      // 函數(shù)接收了一個參數(shù)n    
      fn: function (n) {
        return [
          // h2 標簽的 render 函數(shù)
          e("h2", [t._v("footer 具名 + 作用域插槽")]), 
          // p 標簽的 render 函數(shù),這里可以看到編譯后是:n.footerInfo.text
          e("p", [t._v(t._s(n.footerInfo.text))])
        ]
      }
    }
  ])
}

三、slot實現(xiàn)原理

1. 斷點調(diào)試

為了方便大家看調(diào)試結(jié)果,當前項目的組件結(jié)構(gòu)主要是這樣,有三大層:

Vue -> <App /> -> <my-slot />

這里筆者在運行時代碼 initRender()、renderSlot() 中,打上 debugger ,直接帶大火看看執(zhí)行流程。這里簡單介紹下兩個方法:

  • initRender:獲取 vm.$slot 。組件初始化時執(zhí)行(比如執(zhí)行到 my-slot組件 時,可從vm.$slot 獲取父組件中的slot vNode,也就是我們的 默認插槽)

  • renderSlot:把組件的 slot 替換成真正的 插槽vNode

接下來直接看實驗截圖:

1、先是進入initRender()(這里跳過初始化 大Vue、App 的過程)。直接到初始化 my-slot組件 過程?!?簡單解釋:由于 App組件 執(zhí)行 render 得到 App組件vNode ,在 patch 過程中 遇到 vue-component-my-slot 的 vNode ,又執(zhí)行 my-slot組件 的 初始化流程?!?/p>

  • 我們不難發(fā)現(xiàn),圖中此時正值 my-slot組件init 階段。

Vue.slot原理及slot是如何實現(xiàn)的

  • 再往下執(zhí)行,我們可以得到 App組件中的 <h2>默認插槽</h2> 的vNode,賦值給 vm.$slot(這里我們記住,默認插槽的 vNode 已經(jīng)得到)

Vue.slot原理及slot是如何實現(xiàn)的

2、再是進入 renderSlot()。接著上面繼續(xù)單步執(zhí)行,會走到 renderSlot 中。這時候,已經(jīng)進入到 my-slot組件render 階段了?;仡櫟谝徊街校藭r我們手握 默認插槽的vNode,并存在 vm.$slot.default

header插槽

  • 按順序走,先是 render 排第一的 header 的 vNode。如圖所示,會走到斷點處,我們接著單步

Vue.slot原理及slot是如何實現(xiàn)的

  • 直接進入執(zhí)行我們 header插槽 的render函數(shù)執(zhí)行處。根據(jù)調(diào)試步驟,我們可以肯定,放置在 scopedSlots屬性 中的 render函數(shù),是在子組件 render 的時候執(zhí)行

Vue.slot原理及slot是如何實現(xiàn)的

  • 得到 header插槽 的 vNode

Vue.slot原理及slot是如何實現(xiàn)的

默認插槽

  • 繼續(xù)單步走,這次輪到 默認插槽 了!如圖所示,這里的 key 正是 'default'??梢园l(fā)現(xiàn),這里并沒有像上面 header插槽 一樣,去執(zhí)行 render,而是直接將我們之前得到的 插槽vNode返回了。

Vue.slot原理及slot是如何實現(xiàn)的

  • 得到 default插槽 的 vNode

Vue.slot原理及slot是如何實現(xiàn)的

作用域插槽

  • 前面都跟 header插槽 一致,都是會在 my-slot 組件中 執(zhí)行插槽的 render。我們直接單步到 render 處看看有什么區(qū)別。這里可以得出,function處傳入的參數(shù)正是我們子組件 my-slotdata 數(shù)據(jù),這就是為什么我們在 App組件 能通過 作用域插槽 訪問到子組件數(shù)據(jù)的原因了

Vue.slot原理及slot是如何實現(xiàn)的

  • 最后也是返回 footer插槽 的vNode。好了,驗證過程結(jié)束~

2. 總結(jié)插槽實現(xiàn)原理

其實上面的流程只是論證過程,大家不可以不必深陷其中。筆者在這里直接根據(jù)實踐過程,給大伙總結(jié)出結(jié)論!也就是要回到我們一開始的三個問題!

1、普通插槽、 作用域插槽 的 vNode 是在哪個環(huán)節(jié)生成的,render 父組件時還是子組件時?

  • 默認插槽,不管 v2.5v2.6 的寫法,都是在 父組件中生成 vNodevNode 存在 vm.$slot 中。待子組件 render 到插槽時,會直接拿到 父組件的 vNode

  • 具名插槽兩個版本情況不一。根據(jù)編譯結(jié)果可知:

    • v2.5 的寫法,跟默認插槽是一樣的,在父組件生成vNode,子組件直接拿來用

    • v2.6 中,直接時在 子組件 中才去執(zhí)行 插槽render ,生成 插槽vNode。

  • 作用域插槽。不管版本,都是在子組件中進行render的

  • 大家不妨這么理解,模版編譯后,只要是被放在 scopeSlots屬性 中的插槽,都會在子組件執(zhí)行 render 的時候才會去生成vNode。

2、作用域插槽 為什么能在父組件訪問到子組件的數(shù)據(jù)?

  • 作用域插槽只有子組件render的時候,才會執(zhí)行render生成vNode。并且,作用域插槽的 render 函數(shù)能接參數(shù),從而獲得子組件的數(shù)據(jù)。就是這樣形成了作用域插槽!所以我們能在父組件中,訪問到子組件的data數(shù)據(jù)。

3、普通插槽 跟 作用域插槽 在實現(xiàn)上有區(qū)別嗎?

  • 有區(qū)別。

    • 普通插槽。如果是 v2.5 ,具名插槽 和 默認插槽 都只在 父組件 render 的時候生成 vNode,子組件要 渲染slot 的時候,直接在父組件實例的 $slot 中獲取已經(jīng)是vNode的數(shù)據(jù)。

    • 普通插槽。如果是 v2.6 ,具名插槽 雖然是在子組件中執(zhí)行的 render,但是其不接收參數(shù)。

    • 作用域插槽。不管 v2.5 還是 v2.6,都只在 子組件執(zhí)行 render,并且能接收參數(shù)。

“Vue.slot原理及slot是如何實現(xiàn)的”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實用文章!

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI