一次性講明白vue插槽slot

vue插槽slot

1、前言

vue官方文檔中在"組件基礎"內容中提到組件能夠經過插槽分發內容,那插槽是怎麼使用的呢?它要解決什麼場景的問題呢?javascript

咱們在構建頁面過程當中通常會把用的比較多的公共的部分抽取出來做爲一個單獨的組件,可是在實際使用這個組件的時候卻又不能徹底的知足需求,我但願在這個組件中添加一點東西,這時候咱們就須要用到插槽來分發內容。vue

注意:如下的全部內容是基於vue版本 2.6.0 起java

2、插槽是什麼

下面看一個例子bash

寫一個父組件: test.vueapp

<template>
    <div>
      <div>你們好我是父組件</div>
      <myslot>
        <p>測試一下吧內容寫在這裏了可否顯示</p>
      </myslot>
    </div>
</template>

<script>
  import myslot from './myslot';
  export default {
    components: {
      myslot
    }
  }
</script>

<style>
</style>

複製代碼

寫一個子組件:myslot.vue測試

<template>
  <div>
      <div>我是子組件</div>
  </div>
</template>

<script>
</script>

<style>
</style>
複製代碼

運行代碼,發現,最終渲染的效果是ui

你們好我是父組件
我是子組件
複製代碼

那若是我想實現顯示父組件中p標籤的內容怎麼辦spa

修改子組件:myslot.vuecode

<template>
  <div>
      <div>我是子組件</div>
      <p>如今測試一下slot</p>
      <slot></slot>
  </div>
</template>

<script>
</script>

<style>
</style>

複製代碼

運行代碼,能夠看到如下效果component

你們好我是父組件
我是子組件
如今測試一下slot
測試一下吧內容寫在這裏了可否顯示
複製代碼

官方文檔對於插槽的應用場景是這樣描述的:

咱們常常須要向一個組件傳遞內容

Vue 自定義的 <slot> 元素讓這變得很是簡單

只要在須要的地方加入插槽就好了——就這麼簡單!

結合上面的例子來理解就是這樣的:

1.父組件在引用子組件時但願向子組價傳遞模板內容<p>測試一下吧內容寫在這裏了可否顯示</p>

2.子組件讓父組件傳過來的模板內容在所在的位置顯示

3.子組件中的<slot>就是一個槽,能夠接收父組件傳過來的模板內容,<slot> 元素自身將被替換

4.<myslot></myslot>組件沒有包含一個 <slot> 元素,則該組件起始標籤和結束標籤之間的任何內容都會被拋棄

2、插槽的做用

讓用戶能夠拓展組件,去更好地複用組件和對其作定製化處理

3、插槽的分類

1.默認插槽

在一個 <submit-button> 組件中:

<button type="submit">
  <slot></slot>
</button>
複製代碼

咱們可能但願這個 <button> 內絕大多數狀況下都渲染文本「Submit」,可是有時候卻但願渲染文本爲別的東西,那怎麼實現呢?

咱們能夠將「Submit」做爲後備內容,咱們能夠將它放在 <slot> 標籤內:

<button type="submit">
  <slot>Submit</slot>
</button>
複製代碼

如今當我在一個父級組件中使用 <submit-button> 而且不提供任何插槽內容時:

<submit-button></submit-button> 複製代碼

後備內容「Submit」將會被渲染:

<button type="submit">
  Submit
</button>
複製代碼

可是若是咱們提供內容:

<submit-button>
  Save
</submit-button>
複製代碼

則這個提供的內容將會被渲染從而取代後備內容:

<button type="submit">
  Save
</button>
複製代碼

2.具名插槽

有時咱們寫了一個子組件,咱們但願

<template>
    <div class="container">
      <header>
        <!-- 咱們但願把頁頭放這裏 -->
      </header>
      <main>
        <!-- 咱們但願把主要內容放這裏 -->
      </main>
      <footer>
        <!-- 咱們但願把頁腳放這裏 -->
      </footer>
    </div>
</template>
複製代碼

對於這樣的狀況,<slot> 元素有一個特殊的 attribute:name。這個 attribute 能夠用來定義額外的插槽:

<template>
  <div class="container">
    <header>
      <slot name="header"></slot>
    </header>
    <main>
      <slot></slot>
    </main>
    <footer>
      <slot name="footer"></slot>
    </footer>
  </div>
</template>
複製代碼

一個不帶 name<slot> 出口會帶有隱含的名字「default」。

父組件在向具名插槽提供內容的時候,咱們能夠在一個 <template> 元素上使用 v-slot 指令,並以 v-slot 的參數的形式提供其名稱:

<template>
  <myslot>
    <div>你們好我是父組件</div>
    <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 footer info</p>
    </template>
  </myslot>
</template>

<script>
  import myslot from './myslot';
  export default {
    components: {
      myslot
    }
  }
</script>
<style>
</style>
複製代碼

最終的渲染結果能夠看到

Here might be a page title
你們好我是父組件
A paragraph for the main content.

And another one.

Here's footer info 複製代碼

父組件中會向子組件中具名傳遞對應的模板內容,而沒有指定名字的模板內容會傳遞給子組件中不帶 name<slot>

固然,若是父組件中

<template v-slot:default>
    <p>A paragraph for the main content.</p>
    <p>And another one.</p>
</template>
複製代碼

一樣是傳遞給子組件中不帶 name<slot>

注意:

v-slot 只能添加在 <template>

具名插槽在書寫的時候可使用縮寫,v-slot用#來代替

<template>
  <myslot>
    <div>你們好我是父組件</div>
    <template #header>
      <h1>Here might be a page title</h1>
    </template>

    <p>A paragraph for the main content.</p>
    <p>And another one.</p>

    <template #footer>
      <p>Here's footer info</p>
    </template>
  </myslot>
</template>

<script>
  import myslot from './myslot';
  export default {
    components: {
      myslot
    }
  }
</script>

<style>
</style>
複製代碼

3.做用域插槽

這裏主要解決的是父組件在向子組件插槽傳遞模板內容時存在訪問子組件數據的問題

還記得默認插槽嗎?若是子組件中寫在 <slot> 標籤內後備內容是與 該組件的data屬性雙向數據綁定的

<template>
  <div>
    <span>
      <slot>{{user.firstName}}</slot>
    </span>
  </div>
</template>

<script>
  export default {
    data () {
      return {
        user:{
          firstName:'gerace',
          lastName:'haLi'
        }
      }
    }
  }
</script>
<style>
</style>

複製代碼

父組件在引用子組件時,但願可以換掉備用內容

<template>
  <myslot>{{ user.firstName }}</myslot>
</template>

<script>
  import myslot from './myslot';
  export default {
    components: {
      myslot
    }
  }
</script>
<style>
</style>
複製代碼

運行代碼這時你會發現提示報錯

Property or method "user" is not defined on the instance but referenced during render.
TypeError: Cannot read property 'firstName' of undefined
複製代碼

這裏爲何?vue官方文檔給出了答案

父級模板裏的全部內容都是在父級做用域中編譯的;子模板裏的全部內容都是在子做用域中編譯的

那應該怎麼解決這個問題?

爲了讓 user 在父級的插槽內容中可用,咱們能夠將 user 做爲 <slot> 元素的一個 attribute 綁定上去:

<span>
  <slot v-bind:user="user">
    {{ user.lastName }}
  </slot>
</span>
複製代碼

綁定在 <slot> 元素上的 attribute 被稱爲插槽 prop。如今在父級做用域中,咱們可使用帶值的 v-slot 來定義咱們提供的插槽 prop 的名字:

<template>
  <myslot>
      <template v-slot:default="slotProps">
      {{ slotProps.user.firstName }}
      </template>
  </myslot>
</template>

<script>
  import myslot from './myslot';
  export default {
    components: {
      myslot
    }
</script>
<style>
</style>

複製代碼

上面例子,咱們選擇將包含全部插槽 prop 的對象命名爲 slotProps,但你也可使用任意你喜歡的名字。

針對上面只給默認插槽傳遞模板內容的例子,在寫法上能夠採用默認插槽的縮寫語法

<template>
  <myslot v-slot:default="slotProps">
     {{ slotProps.user.firstName }}
  </myslot>
</template>

<script>
  import myslot from './myslot';
  export default {
    components: {
      myslot
    }
</script>
<style>
</style>
複製代碼

上面代碼也能夠直接改成

<template>
  <myslot v-slot="slotProps">
     {{ slotProps.user.firstName }}
  </myslot>
</template>
複製代碼

注意:

默認插槽的縮寫語法不能和具名插槽混用,由於它會致使做用域不明確:

<template>
  <myslot v-slot="slotProps">
     {{ slotProps.user.firstName }}
     <template v-slot:other="otherSlotProps">
   		slotProps is NOT available here
     </template>
  </myslot>
</template>
複製代碼

下面再看一下多個插槽的狀況

子組件

<template>
  <div>
    <span>
      <slot v-bind:userData="user" name="header">
        {{ user.msg }}
      </slot>
      <slot v-bind:hobbyData="hobby" name="footer">
        {{ hobby.fruit }}
      </slot>
    </span>
  </div>
</template>

<script>
  export default {
    data () {
      return {
        user:{
          firstName: 'gerace',
          lastName: 'haLi',
        },
        hobby:{
          fruit: "apple",
          color: "blue"
        }
      }
    }
  }
</script>
<style>
</style>
複製代碼

父組件

<template>
  <myslot>
      <template v-slot:header="slotProps">
        {{ slotProps.userData.firstName }}
      </template>
      <template v-slot:footer="slotProps">
        {{ slotProps.hobbyData.fruit }}
      </template>
  </myslot>
</template>

<script>
  import myslot from './myslot';
  export default {
    components: {
      myslot
    }
</script>
<style>
</style>

複製代碼

針對多個插槽的狀況,在寫法上能夠解構插槽prop,父組件的寫法以下

<template>
  <myslot>
      <template v-slot:header="{userData}">
        {{ userData.firstName }}
      </template>
      <template v-slot:footer="{hobbyData}">
        {{ hobbyData.fruit }}
      </template>
  </myslot>
</template>

<script>
  import myslot from './myslot';
  export default {
    components: {
      myslot
    }
  }
</script>
<style>
</style>
複製代碼

在具名插槽的介紹部分有講過,具名插槽可使用縮寫,v-slot可使用#來代替,因此以上代碼能夠寫成:

<template>
  <myslot>
      <template #header="{userData}">
        {{ userData.firstName }}
      </template>
      <template #footer="{hobbyData}">
        {{ hobbyData.fruit }}
      </template>
  </myslot>
</template>

<script>
  import myslot from './myslot';
  export default {
    components: {
      myslot
    }
  }
</script>
<style>
</style>
複製代碼

可是須要注意的是該縮寫只在其有參數的時候纔可用。這意味着如下語法是無效的:

<!-- 這樣會觸發警告 -->
<template> <myslot> <template #="{userData}"> {{ userData.firstName }} </template> <template #="{hobbyData}"> {{ hobbyData.fruit }} </template> </myslot> </template>
複製代碼
相關文章
相關標籤/搜索