iT邦幫忙

2017 iT 邦幫忙鐵人賽
DAY 16
1
Modern Web

Vue.js 30天系列 第 16

Vue.js 16 - 組件/元件(Component) - 樣板宣告及動態插入內容

前面介紹過

  • data (宣告內部狀態)
  • props (宣告對外介面 - 用於傳入資料)
  • event (觸發父層事件 - 用於傳出資料)

接下來要介紹 樣板(template),用於呈現資料。

Vue.js 05 - 資料綁定(data binding) 與 樣板(template) 語法 已介紹如何綁定資料與樣板。
這篇打算講解已寫好的樣板,如何加入組件/元件(component)宣告。


靜態樣板

String

第一種,組字串

Vue.component('child', {
    template: '<ul><li v-for="item in list">{{ item.name }}</li></ul>'
    /* JS字串串接 */
    /*
    template: [
        '<ul>',
        '  <li v-for="item in list">{{ item.name }}</li>',
        '</ul>'].join('');
    */
});

<template>

Hello.vue

<template>
    <ul>
        <li v-for="item in list">{{ item.name }}</li>
    </ul>
</template>

Vue.component('child', {
    /* 免寫,vue-loader 會幫你補上 template 屬性 */
});

Vue 1.x用,寫在html,指定<template>

<template id="child-template">
    <ul>
    <li v-for="item in list">{{ item.name }}</li>
    </ul>
</template>

Vue.component('child', {
    template: '#child-template'
});

Vue 2.x用,寫在<script>x-template,指定其名

<script type="text/x-template" id="child-template">
  <p>Hello hello hello</p>
</script>

Vue.component('child', {
    template: '#child-template'
});

不好看,但網路發問用得到。


動態插入內容

一開始做component時,想得沒那麼多,需求很簡單。

<app></app>
<template>
    <div>
        <!-- 最初的樣板定義 -->
    </div>
</template>

<script>
export default {
    /* ... */
}
</script>

後來需求逐漸增加,神說<app>有了<header><footer>,我們就得生給他,App.vue變成

<template>
    <div>
      <!-- 最初的樣板定義 -->
      <app-header></app-header>
      <app-footer></app-footer>
    </div>
</template>

<script>
export default {
    /* ... */
    components: {
        AppHeader,
        AppFooter
    },
    /* ... */
}
</script>

組件內容未必固定,若是我們兩個版本的<app>都要?
為了少部分的差異,多做一個組件,有點脫褲子放屁。

要怎麼讓組件樣板具有彈性呢?
像是這樣

<app>
  <app-header></app-header>
  <app-footer></app-footer>
</app>

解法一 <slot>

ng1 有 transclude,Vue 給你 <slot>

<template>
    <div>
      <!-- 最初的樣板定義 -->
      <!-- <app>樣板留下插座,供動態插入 -->
      <slot></slot>
    </div>
</template>
<app>
  <!-- 如此一來,只要接上插座 -->
  <!-- <app>元素內的html會插入<slot>的位置 -->
  <app-header></app-header>
  <app-footer></app-footer>
</app>

有時插入內容想放在不同位置,給它個名字 - 具名<slot>

<template>
    <div class="modal fade">
        <div class="modal-dialog" role="document">
            <div class="modal-content">
                <div class="modal-header">
                    <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
                    <slot name="modal-header">
                </div>
                <div class="modal-body">
                    <slot name="modal-body">
                </div>
                <div class="modal-footer">
                    <slot name="modal-footer">
                </div>
            </div><!-- /.modal-content -->
        </div><!-- /.modal-dialog -->
    </div><!-- /.modal -->
</template>
<modal>
    <h4 slot="modal-header" class="modal-title">Modal title</h4>
    
    <p slot="modal-body">One fine body&hellip;</p>
    
    <button slot="modal-footer" type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
    <button slot="modal-footer" type="button" class="btn btn-primary">Save changes</button>
</modal>

團隊成員不會動到你的<app>組件,把省下的merge時間拿去泡咖啡。

解法二 - inline-template

同樣寫法,但這種作法會完全覆蓋掉<app>原始內容

<app inline-template>
  <div>
    <p>These are compiled as the component's own template.</p>
    <p>Not parent's transclusion content.</p>
  </div>
</app>

等校於

  <!-- <app>模板原始內容不見了 -->
  <div>
    <p>These are compiled as the component's own template.</p>
    <p>Not parent's transclusion content.</p>
  </div>

動態插入內容還欠作用域待補,晚點再回來看吧


上一篇
Vue.js 15 - 組件/元件(Component) - 自訂事件及對外溝通
下一篇
Vue.js 17 - 生命週期(Lifecycle)
系列文
Vue.js 30天30

尚未有邦友留言

立即登入留言