iT邦幫忙

2023 iThome 鐵人賽

DAY 25
0
Vue.js

業主說給你30天學會Vue系列 第 25

V25_Vue的slot網頁內容的分解及組合

  • 分享至 

  • xImage
  •  

V25_Vue的slot網頁內容的分解及組合

今天來研究一下 slots的用法

參考W3School Vue的說明
Vue v-slot
https://www.w3schools.com/vue/vue_v-slot.php

slots 的概念可以想成是要將在父元件 component 的內容傳送到 component 的本身

之前是 是將父元件中的component的屬性傳送到子元件,再回傳 子元件的<template>的內容回到父元件。
這時如果要較多的網頁內容的話,可以將slot當作是設定好的樣版,
這時傳送要組合的部份內容到子元件的<slot>元素中,就可以回傳完整的網頁內容了

來看一下範例

main.js

import { createApp } from 'vue'

import App from './App.vue'
import SlotComp from './components/SlotComp.vue'

const app = createApp(App)
app.component('slot-comp', SlotComp)
app.mount('#app')

先看到
import SlotComp from './components/SlotComp.vue' app.component('slot-comp', SlotComp)

SlotComp -> 'slot-comp'

App.vue

<template>
  <h1>App.vue</h1>
  <p>The component has two <div> tags with one <slot> in each.</p>
  <slot-comp v-slot:default>'Default slot'</slot-comp>
</template>

<script></script>

<style>
  #app {
    width: 300px;
  }
</style>                  

接著看到 <slot-comp v-slot:default>'Default slot'</slot-comp>
透過 v-slot:default 來跟 子元件的 <slot></slot>綁定
若是 v-slot:bottomSlot 則是 跟子元件的 <slot name="bottomSlot"></slot>綁定

接著是 <slot-comp v-slot:default>'Default slot'</slot-comp>
代表 將內容 'Default slot' 傳到 <slot></slot>

SlotComp.vue

<template>
    <h3>Component</h3>
    <div>
        <slot></slot>
    </div>
    <div>
        <slot name="bottomSlot"></slot>
    </div>
</template>

<script></script>

<style scoped>
    div {
        height: 30px;
        width: 50%;
        border: dotted black 1px;
        margin: 10px;
        padding: 10px;
        background-color: lightgreen;
        font-weight: bold;
    }
</style>  

最後的結果是

<div id="app" data-v-app="">
  <h1>App.vue</h1>
  <p>The component has two <div> tags with one <slot> in each.</p>
  <h3 data-v-c9ce8e5e="">Component</h3>
  <div data-v-c9ce8e5e="">'Default slot'</div>
  <div data-v-c9ce8e5e=""></div>
</div>

<slot-comp v-slot:default>'Default slot'</slot-comp>

轉換後

<h3 data-v-c9ce8e5e="">Component</h3>
<div data-v-c9ce8e5e="">'Default slot'</div>
<div data-v-c9ce8e5e=""></div>

//-----------------------------------------
再看一下另一個範例

App.vue

<template>
  <h1>App.vue</h1>
  <p>The component has two div tags with one slot in each.</p>
  <slot-comp>
    <template v-slot:bottomSlot>
      <h4>To the bottom slot!</h4>
      <p>This p tag and the h4 tag above are directed to the bottom slot with the v-slot directive used on the template tag.</p>
    </template>
    <p>This goes into the default slot</p>
  </slot-comp>
</template>

SlotComp.vue

<template>
    <h3>Component</h3>
    <div>
        <slot></slot>
    </div>
    <div>
        <slot name="bottomSlot"></slot>
    </div>
</template>

最後的輸出是

<div id="app" data-v-app="">
  <h1>App.vue</h1>
  <p>The component has two div tags with one slot in each.</p>
  <h3 data-v-66527a85="">Component</h3>
  <div data-v-66527a85="">
    <p>This goes into the default slot</p>
  </div>
  <div data-v-66527a85="">
    <h4>To the bottom slot!</h4>
    <p>This p tag and the h4 tag above are directed to the bottom slot with the v-slot directive used on the template tag.</p>
  </div>
</div>

由以上 可看出 <p>This goes into the default slot</p> 對應到 <slot></slot>

<template v-slot:bottomSlot>
  <h4>To the bottom slot!</h4>
  <p>This p tag and the h4 tag above are directed to the bottom slot with the v-slot directive used on the template tag.</p>
</template> 

對應到 <slot name="bottomSlot"></slot>

整理一下,可以這樣來解讀

<slot-comp> </slot-comp>

引入

<h3>Component</h3>
<div>
    <slot></slot>
</div>
<div>
    <slot name="bottomSlot"></slot>
</div>

<slot-comp> 中的內容可看成是要傳入的內容
<slot-comp> 要傳入的內容 </slot-comp>

其中 將 <template v-slot:bottomSlot> 的部份 傳到 <slot name="bottomSlot"></slot>
<p>This goes into the default slot</p> 傳到 <slot></slot>

因此,在 <slot-comp> 中的內容,並不是排版的順序,只是內容的範圍

<slot-comp>
  <template v-slot:bottomSlot>
    <h4>To the bottom slot!</h4>
    <p>This p tag and the h4 tag above are directed to the bottom slot with the v-slot directive used on the template tag.</p>
  </template>
  <p>This goes into the default slot</p>
</slot-comp>

若改成

<slot-comp>
  <template v-slot:bottomSlot>  //-- bottomSlot slot
    <h4>To the bottom slot!</h4>
    <p>This p tag and the h4 tag above are directed to the bottom slot with the v-slot directive used on the template tag.</p>
  </template>
  <template>  //-- default slot
    <p>This goes into the default slot</p>
  </template>
</slot-comp>

就會比較清楚

整理一下 slot的寫法

<slot-comp v-slot:default>     -> <slot></slot>
<slot-comp v-slot:bottomSlot>  -> <slot name="bottomSlot"></slot>

<slot-comp>
  <template v-slot:bottomSlot>
  </template>
</slot-comp>

slot 的簡約寫法

<slot-comp>
<slot-comp #bottomSlot>

<slot-comp>
  <template #bottomSlot>
  </template>
</slot-comp>
v-slot:bottomSlot -> #bottomSlot
v-on:click -> @click
v-bind:src -> :src
v-mode:value -> 無簡約寫法

//-------
最後再看一個範例

App.vue

<template>
  <h1>Scoped Slots</h1>
  <p>App.vue controls how local data from the scoped slot is rendered.</p>
  <hr>
  <slot-comp v-slot="texts">
    <h2>{{ texts.staticText }}</h2>
    <p class="greenP">{{ texts.dynamicText }}</p>
  </slot-comp>
</template>

SlotComp.vue

<template>
    <slot 
        staticText="This text is static"
        :dynamicText="text"
    ></slot>
</template>

<script>
export default {
    data() {
        return {
            text: 'This text is from the data property'
        }
    }
}
</script>

在App.vue

  <slot-comp v-slot="texts">
    <h2>{{ texts.staticText }}</h2>
    <p class="greenP">{{ texts.dynamicText }}</p>
  </slot-comp>

在SlotComp.vue
<slot staticText="This text is static" :dynamicText="text"></slot>

合併後為

<div id="app" data-v-app="">
  <h1>Scoped Slots</h1>
  <p>App.vue controls how local data from the scoped slot is rendered.</p>
  <hr>
  <h2>This text is static</h2>
  <p class="greenP">This text is from the data property</p>
</div>

<slot staticText="This text is static" :dynamicText="text"></slot>
這是 代表回傳的變數為 staticText, :dynamicText

同時 :dynamicText 綁定到 text

傳送回 <slot-comp v-slot="texts">texts變數來接收資料

texts.staticText, texts.dynamicText

最後 顯示到 {{ texts.staticText }},{{ texts.dynamicText }}

//-------------------------------
還是要整理一下
父元件

<slot-comp v-slot:bottomSlot>  ->  <slot-comp #bottomSlot>

<slot-comp v-slot:bottomSlot="texts">  ->  <slot-comp #bottomSlot="texts">

<slot-comp v-slot="texts">

<slot-comp>
  <template v-slot:bottomSlot></template>
</slot-comp>

子元件
<slot name="bottomSlot"></slot> 這個是名稱設定為 bottomSlot 的slot

有關 slot 的部份,
因為整個流程 像是來回的內容綁定,連動,很容易造成混淆,
在使用上要很清楚之間的關係
可以先從簡單的寫法,再擴展成複雜的寫法

//-------------------------
這個範例可以釐清一些想法

App.vue

<slot-comp v-slot="food">
  <h2>{{ food.key+", "+food.fname }}</h2>
</slot-comp>

SlotComp.vue
<slot :key="i" :fname="x" v-for="(x, i) in foods"></slot>

data() {
  return {
    foods: ['Apple','Pizza','Rice','Fish']
  }
}

其中
<slot :key="i" :fname="x" v-for="(x, i) in foods"></slot>

會變成

<slot key="0" fname="Apple"></slot>
<slot key="1" fname="Pizza"></slot>
<slot key="2" fname="Rice"></slot>
<slot key="3" fname="Fish"></slot>

<slot-comp v-slot="food">會作用到每一個 <slot>

<slot>的屬性回傳回<slot-comp> 的變數 food
然後呈現在 <h2>{{ food.key+", "+food.fname }}</h2>

最後變成

<h2>0, Apple</h2>
<h2>1, Pizza</h2>
<h2>2, Rice</h2>
<h2>3, Fish</h2>

所以可以看作是

<slot-comp v-slot="food">
  <h2>{{ food.key+", "+food.fname }}</h2>
</slot-comp>

其中 food 用來讀取 <slot key="0" fname="Apple"></slot> 上的 屬性

若是 <slot key="0" fname="Apple"></slot> 本身沒有內容,就會是將
屬性 key="0" fname="Apple" 傳回 <slot-comp v-slot="food">

另外一種是 <slot-comp v-slot="food"></slot-comp> 本身沒有內容

<slot key="0" fname="Apple">ABCDE</slot>
這時 <slot-comp> 呈現的就是 <slot> 的內容

若是<slot-comp>要傳給 <slot>資料的話,語法就是

App.vue

<slot-comp foodName="Apple" fid="0"></slot-comp>
<slot-comp foodName="Pizza" fid="1"></slot-comp>
<slot-comp foodName="Rice" fid="2"></slot-comp>
<slot-comp foodName="Fish" fid="3"></slot-comp>

SlotComp.vue

<slot><h2>{{ fid+", "+foodName }}</h2></slot>
props: ['foodName','fid']

同樣也得到

<h2>0, Apple</h2>
<h2>1, Pizza</h2>
<h2>2, Rice</h2>
<h2>3, Fish</h2>

結論就是Vue提供了元件之間不同資料傳送的方式
以及產生多個元件的方式,
漸漸地要進入活用的練習了。


上一篇
V24_Vue的props_emit_子元件傳資料給父元件
下一篇
V26_Vue的Routing的操作
系列文
業主說給你30天學會Vue31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言