Vue – 如何使用作用域插槽 (Scoped Slots) 分割元件版面並餵入不同資料

這次我們示範要如何使用 slot 去製作分割組件。我希望呈現

  • <header> + <main>
  • <main> 包含了標題與三篇文章
  • 三篇文章是從伺服器取得資料

影片教學

文字教學如下

1、建立 container 並加入第一個整體架構的組件

<!-- 先建立父組件,形成整體框架 -->
<template id="container">
    <div class="container">
        <header>
            <ul>
                <li>Home</li>
                <li>About</li>
            </ul>
        </header>
    </div>
</template>

<div class="app">
    <c-container></c-container>
</div>
// 建立第一個父組件
Vue.component('c-container', {
    template: '#container'
})

// 先初始化後
var vm = new Vue({
    el: '.app'
})

2、在父組件中,建立一個子組件,子組件有自己的樣式

<!-- 模版使用 main 包圍 -->
<template id="content">
    <main>
        <h1>瀏覽器</h1>
        <p>文字摘要介紹</p>
    </main>
</template>

<template id="container">
    <div class="container">
        <header>
            <ul>
                <li>Home</li>
                <li>About</li>
            </ul>
        </header>

        <!-- 加入子組件,放來放置主內容 -->
        <c-content></c-content>
    </div>
</template>

<div class="app">
    <c-container></c-container>
</div>
// 加入子組件
Vue.component('c-content', {
    template: '#content'
})

Vue.component('c-container', {
    template: '#container'
})

var vm = new Vue({
    el: '.app'
})

這是父組件的框架 + 子組件的內容樣式

3、父組件,建立準備分派到子組件的內容,也就是一個模版

<template id="content">
    <main>
        <h1>瀏覽器</h1>
        <p>文字摘要介紹</p>
    </main>
</template>

<template id="container">
    <div class="container">
        <header>
            <ul>
                <li>Home</li>
                <li>About</li>
            </ul>
        </header>

        <!-- 1.加入子組件,放來放置主內容 -->
        <c-content>
            <!-- 2.使用 template 包圍,加入 scope 範圍屬性,指定一個暫時參數叫做屬性 props -->
            <template scope="props">
                <!-- 3.準備批次顯示的模版樣式 -->
                <article>
                    <h1>Title</h1>
                    <p>Content</p>
                </article>
            </template>
        </c-content>
    </div>
</template>

<div class="app">
    <c-container></c-container>
</div>

4. 在子組件,建立插槽 slot 的擺放位置

<template id="content">
    <main>
        <h1>瀏覽器</h1>
        <p>文字摘要介紹</p>
        <!-- 1.放置插槽,命名為 slot-content -->
        <slot name="slot-content"></slot>
    </main>
</template>

<template id="container">
    <div class="container">
        <header>
            <ul>
                <li>Home</li>
                <li>About</li>
            </ul>
        </header>

        <c-content>
            <!-- 2. 替模版指定對應的插槽為 slot-content -->
            <template scope="props" slot="slot-content">
                <article>
                    <h1>Title</h1>
                    <p>Content</p>
                </article>
            </template>
        </c-content>
    </div>
</template>

<div class="app">
    <c-container></c-container>
</div>

5. 在子組件建立 JSON 資料,例如透過 AJAX 遠端取得。

Vue.component('c-content', {
    template: '#content',
    // 假設從遠端取得了物件,使用 artlist 包裝
    data: function (){
        return {
            artlist: [
                {title: 'Chrome', description: 'Chrome 描述...'},
                {title: 'IE', description: 'IE 描述...'},
                {title: 'Firefox', description: 'Firefox 描述...'},
            ]
        }
    }
})

Vue.component('c-container', {
    template: '#container'
})

var vm = new Vue({
    el: '.app'
})

6. 修改子組件,讓插槽使用 for loop 逐一取出每筆資料

<template id="content">
    <main>
        <h1>瀏覽器</h1>
        <p>文字摘要介紹</p>
        <!-- 1. 每筆資料從 artlist 物件逐一取出,每筆用 val 承接 -->
        <!-- 將 val 放入自訂參數 item -->
        <slot name="slot-content" :item="val" v-for="val in artlist"></slot>
    </main>
</template>

<template id="container">
    <div class="container">
        <header>
            <ul>
                <li>Home</li>
                <li>About</li>
            </ul>
        </header>

        <c-content>
            <template scope="props" slot="slot-content">
                <!-- 2. 範圍使用了自訂參數名稱叫做 props-->
                <!-- 所以子組件模版中的 item 會被放到 props 底下 -->
                <article>
                    <h1>{{ props.item.title }}</h1>
                    <p>{{ props.item.description }}</p>
                </article>
            </template>
        </c-content>
    </div>
</template>

<div class="app">
    <c-container></c-container>
</div>

7、最後完成

<template id="content">
    <main>
        <h1>瀏覽器</h1>
        <p>文字摘要介紹</p>
        <slot name="slot-content" :item="val" v-for="val in artlist"></slot>
    </main>
</template>

<template id="container">
    <div class="container">
        <header>
            <ul>
                <li>Home</li>
                <li>About</li>
            </ul>
        </header>

        <c-content>
            <template scope="props" slot="slot-content">
                <article>
                    <h1>{{ props.item.title }}</h1>
                    <p>{{ props.item.description }}</p>
                </article>
            </template>
        </c-content>
    </div>
</template>

<div class="app">
    <c-container></c-container>
</div>
Vue.component('c-content', {
    template: '#content',
    data: function (){
        return {
            artlist: [
                {title: 'Chrome', description: 'Chrome 描述...'},
                {title: 'IE', description: 'IE 描述...'},
                {title: 'Firefox', description: 'Firefox 描述...'},
            ]
        }
    }
})

Vue.component('c-container', {
    template: '#container'
})

var vm = new Vue({
    el: '.app'
})

發表迴響