Vue3 中插槽(slot)的用法


概要

Vue3(其實從2.6開始)中引入了一個新的指令v-slot,用來表示具名插槽和默認插槽

基礎示例

<!-- default slot -->
<foo v-slot="{ msg }">
    {{ msg }}
</foo>

<!-- named slot -->
<foo>
    <template v-slot:one="{msg}">
        {{ msg }}
    </template> 
</foo>

為什么要這么做

在v2.5中介紹了slot-scope,可以直接在插槽元素上使用。

<foo>
    <div slot-scope="{ msg }">
        {{ msg }}
    </div>
</foo>

同樣的,我們也可以在組件上這樣使用。

<foo>
    <bar slot-scope="{ msg }">
        {{ msg }}
    </bar>
</foo>

然而,上面的示例會導致一個問題:slot-scope的位置不能清晰的反映出這個作用域變量是哪一個 組件提供的。上面例子中slot-scope是寫在<bar>組件上,但是實際上這個作用域變量的定義是有<foo>組件的默認插槽提供的。
當嵌套的層級越深,情況就會變得越糟。

<foo>
  <bar slot-scope="foo">
    <baz slot-scope="bar">
      <div slot-scope="baz">
        {{ foo }} {{ bar }} {{ baz }}
      </div>
    </baz>
  </bar>
</foo>

像上面這種情況,我們無法立即分辨出模板上作用域變量是由那一個組件提供的。

細節設計

引入一個新的指令v-slot

我們可以在slot容器<template>上使用v-slot來表示一個傳入組件的插槽,通過指令參數來表示插槽的名稱。

<foo>
    <template v-slot:header>
        <div class="header"></div>
    </template>
    <template v-slot:body>
        <div class="body"></div>
    </template>
    <template v-slot:footer>
        <div class="footer"></div>
    </template>
</foo>

作用域插槽的內部工作原理是將插槽的內容包裹在一個函數里

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

這就意味着v-slot的值實際上可以是任何能夠作為函數定義中的參數的Javascript表達式,所以在支持的環境下 (單文件組件或現代瀏覽器),你也可以使用 ES2015 解構來傳入具體的插槽 prop,如下:

<foo>
    <template v-slot:header="{ msg }">
        <div class="header">
            Message from header slot: {{ msg }}
        </div>
    </template>
</foo>

v-slot可以直接用在組件上,如果沒有參數,則表示默認的作用域插槽,傳遞給默認插槽的屬性應該作為變量在其屬性值聲明。

<foo v-slot="{ msg }">
    {{ msg }}
</foo>

新舊語法對比

  • 對於常用的場景(只有一個默認的作用域插槽)

    <foo v-slot="{msg}">{{msg}}</foo>
    
  • 作用域變量與組件之間的聯系更加的清晰

    <!-- old -->
    <foo>
      <bar slot-scope="foo">
        <baz slot-scope="bar">
          <div slot-scope="baz">
            {{ foo }} {{ bar }} {{ baz }}
          </div>
        </baz>
      </bar>
    </foo>
    

    使用新語法也可以達到同樣的效果

    <foo v-slot="foo">
      <bar v-slot="bar">
        <baz v-slot="baz">
          {{ foo }} {{ bar }} {{ baz }}
        </baz>
      </bar>
    </foo>
    

注意:組件提供的作用域變量依然聲明在當前組件上,新的語法更清晰的展示出組件與作用域變量的關系。(父級模板里的所有內容都是在父級作用域中編譯的;子模板里的所有內容都是在子作用域中編譯的。)

動態插槽名

動態指令參數也可以用在 v-slot 上,來定義動態的插槽名:

<foo>
    <template v-slot:[slotName]>
        ...
    </template>
</foo>

插槽指令的縮寫

v-bindv-on相似,縮寫只有在存在參數時才生效,這就意味着v-slot沒有參數時不能使用#=,對於默認插槽,可以使用#default來代替v-slot

<!-- full syntax -->
<foo>
  <template v-slot:header="{ msg }">
    Message from header: {{ msg }}
  </template>

   <template v-slot:footer>
    A static footer
  </template>
</foo>

<!-- shorthand -->
<foo>
  <template #header="{ msg }">
    Message from header: {{ msg }}
  </template>

   <template #footer>
    A static footer
  </template>
</foo>
<foo v-slot="{ msg }">
  {{ msg }}
</foo>

<foo #default="{ msg }">
  {{ msg }}
</foo>

更多的用例對比

默認的文本作用域插槽

<!-- old -->
<foo>
    <template slot-scope="{ msg }">
        {{ msg }}
    </template>
</foo>

<!-- new -->
<foo v-slot="{ msg }">
    {{ msg }}
</foo>

默認的含DOM節點的插槽

<!-- old -->
<foo>
    <div slot-scope="{msg}">
        {{msg}}
    </div>
</foo>

<!-- new -->
<foo v-slot="{msg}">
    <div>
        {{msg}}
    </div>
</foo>

嵌套的默認插槽

<!-- old -->
<foo>
    <bar slot-scope="foo">
        <baz slot-scope="bar"> 
            <template slot-scope="baz">
                {{ foo }} {{ bar }} {{ baz }}
            </template>
        </baz>
    </bar>
</foo>

<!-- new -->

<foo v-slot="foo">
    <bar v-slot="bar">
        <baz v-slot="baz">
            {{ foo }} {{ bar }} {{ baz }}
        </baz>
    </bar>
</foo>

命名插槽

<!-- old -->
<foo>
    <template slot="one" slot-scope="{ msg }">
        text slot: {{ msg }}
    </template>
    <div slot="two" slot-scope="{ msg }">
        element slot: {{ msg }}
    </div>
</foo>

<!-- new -->
<foo>
    <template v-slot:one="{ msg }">
        text slot:{{msg}}
    </template>
    <template v-slot:two="{msg}">
        <div>
            element slot: {{msg}}
        </div>
    </template>
</foo>

嵌套 & 命名 / 默認

<!-- old -->
<foo>
  <bar slot="one" slot-scope="one">
    <div slot-scope="bar">
      {{ one }} {{ bar }}
    </div>
  </bar>

  <bar slot="two" slot-scope="two">
    <div slot-scope="bar">
      {{ two }} {{ bar }}
    </div>
  </bar>
</foo>

<!-- new -->
<foo>
  <template v-slot:one="one">
    <bar v-slot="bar">
      <div>{{ one }} {{ bar }}</div>
    </bar>
  </template>

  <template v-slot:two="two">
    <bar v-slot="bar">
      <div>{{ two }} {{ bar }}</div>
    </bar>
  </template>
</foo>


免責聲明!

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



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