Vue.js (9.2) - 元件(Component)

Vue.js

接續上一篇,接下來要來瞭解如何透過事件向父元件傳遞訊息,以及如何使用插槽,還有動態元件怎麼用。

子元件透過自訂事件告知父元件

再來看一次 props down, events up。我們已經可以將資料從父元件傳給子元件了,接下來子元件要透過事件把資料傳給父元件。

使用 v-on 綁定自訂事件

每個 Vue 實體實作了事件介面(Event Interface),因此可以: 在模板裡是使用 v-on 綁定事件。
使用範例:
HTML<div id="vm">
  <!-- 事件偵聽器 v-on:roll 縮寫為 @roll -->
  <!-- 事件處理函式 showResult -->
  <dice-button @roll="showResult"></dice-button>
  <p>Result: {{ value }}</p>
</div>

<script>
Vue.component('dice-button', {
  template: '<button @click="rollChild">Roll</button>',
  methods: {
    rollChild: function(){
      // 取 1~6 的亂數
      var value = Math.floor(Math.random() * 6) + 1;
      // 觸發父元件的事件(如果有的話),並附上值
      this.$emit('roll', value)
    }
  }
})

new Vue({
  el: '#vm',
  data: {
    value: 1
  },
  methods: {
    showResult: function (v) {
      this.value = Number(v)
    }
  }
})
</script>
我們建立了一個骰子按鈕元件,元件樣板是一個按鈕元素(子元件),偵聽 click 事件,當事件發生時由 rollChild 事件處理函式處理。該函式會取得 1~6 的隨機亂數,然後使用 $emit 方法來觸發父元件的事件,當然我們並不知道父元件是不是有偵聽這個事件,但是我們並不在意,子元件只負責觸發該事件,父元件要不要偵聽是它的事。我們同時將骰子的結果附在事件觸發上送出去。

在父元件中,我們偵聽由子元件發送的 roll 事件,並交由事件處理函式 showResult 去處理,它將得到的值更新到資料屬性中,和該屬性綁定的 view 會因此更新它的顯示。
元件綁定原生事件
有時候你會想要在元件上綁定原生事件,你可以加上 .native 修飾符號:
JavaScript<dice-button @roll="showResult" @click.native="doIt">

使用插槽(Slot)放置內容

每個元件都有它們各自的模板,當元件和元件組合時,該如何決定如何混合來自不同元件的模板,Vue.js 提供了一個 <slot> 元素,可以當成原始內容的插槽。

元件作用範圍

在使用 Slot 之前,要先瞭解元件的作用範圍。

元件的作用範圍是指元件在被編譯時,只能看到自己內部的資料屬性。元件不會被假設自己是父元件(包含其他子元件),或是子元件(被其他元件包含),所以元件永遠只能存取自己內部的資料屬性。

因此,看看以下範例:
HTML<div id="vm">  
  <box>
    <!-- 
    這裡的內容都屬於父元件的,
    因為我們還沒用到 slot,
    所以編譯時這裡的內容都會被丟掉
    -->
    A:{{ name }}
  </box>
  B:{{ name }}
</div>

<script>
Vue.component('box', {
  template: '<div>C:{{ name }}</div>',
  data: function () {
    return {
      name: 'BOX'
    }
  }
})

new Vue({
  el: '#vm',
  data: {
    name: 'VM'
  }
})
</script>
瀏覽器看到的結果:
HTML<div id="vm">
  <div>C:BOX</div>
  B:VM
</div>
<box></box> 會被編譯成它的樣板內容,並且使用它自己的資料屬性,所以是 <div>BOX</div>。這裡沒看到 A:{{ name }} 的內容,它被子元件丟掉了,看看接下來使用 slot 的情況就會比較清楚。

使用 slot

將前面的範例中加上 slot 元素(插槽):
JavaScript// ...略
Vue.component('box', {
  template: '<div><slot></slot>{{ name }}</div>',
// ...略
現在瀏覽器看到的是:
HTML<div id="vm">
  <div>A:VM BOX</div>
  B:VM
</div>
<slot></slot> 是一個插槽,可以讓父元件把資料放入這裡,你可以看到顯示的結果是 A:VM ,表示它的資料是父元件的,而不是子元件的。

slot 的功能就像是一個預先設置的容器,當父元件有指定資料時,就把它放進去,如果元件中只設定一個 slot ,那父元件中所有指定的資料會全部放在其中。而且 slot 可以指定預設值,如果父元件沒有指定資料,就會顯示預設內容。
使用範例:
HTML<div id="vm">  
  <h1>標題H1</h1>
  <box>
    <p>第一段</p>
    <p>第二段</p>
    <p>不管幾段,全都會放入 slot 中</p>
  </box>
</div>

<script>
Vue.component('box', {
  template: '\
  <div>\
    <h2>標題H2</h2>\
    <slot>預設內容</slot>\
  </div>'
})

new Vue({
  el: '#vm'
})
</script>
註:JavaScript 的字串斷行是使用倒斜線 \ ,而且其後不能有任何字元,包含空白都不行。

瀏覽器看到的結果:
HTML<div id="vm">
  <h1>標題H1</h1>
  <div>
    <h2>標題H2</h2>
    <p>第一段</p>
    <p>第二段</p>
    <p>不管幾段,全都會放入 slot 中</p>
  </div>
</div>
<slot>預設內容</slot> 被置換成父元件指定的內容了。

有名稱的 slot

slot 可以使用 name 屬性加上名稱,這樣父元件就能指定要將資料放入哪個 slot 中。
HTML<div id="vm">  
  <box>
    <p slot="section1">第一段</p>
    <p slot="section2">第二段</p>
    <p>其他內容</p>
    <p>很多其他內容</p>
  </box>
</div>

<script>
Vue.component('box', {
  template: '\
  <div>\
    <slot name="section1"></slot>\
    <slot></slot>\
    <slot name="section2"></slot>\
  </div>'
})

new Vue({
  el: '#vm'
})
</script>
顯示結果:
HTML第一段
其他內容
很多其他內容
第二段

動態元件

Vue.js 提供一個特殊的元素叫做 <component>,它有一個 is 屬性,我們可以動態的綁定它,這樣就可以把多個元件掛在這個 <component> 元素上,達到動態切換不同元件的功能。
使用範例:
HTML<div id="vm">
  <button @click="goto('home')">Home</button>
  <button @click="goto('news')">News</button>
  <button @click="goto('about')">About</button>
  <hr>
  <component :is="currentView"></component>
</div>

<script>
Vue.component('home', {
  template: '<div>首頁</div>'
})

Vue.component('news', {
  template: '<div>最新消息</div>'
})

Vue.component('about', {
  template: '<div>關於</div>'
})

new Vue({
  el: '#vm',
  data: {
    currentView: 'home'
  },
  methods: {
    goto: function(v) {
      this.currentView = v
    }
  }
})
</script>
我們建立 3 個元件分別為 homenewsabout,接著設定資料屬性 currentView 的值為 home,表示預設顯示 home。然後加入一個 goto 方法,接受元件的名稱為參數,並將 HTML 中的 3 個按鈕事件呼叫它。當按下按鈕,改變了 currentView 的值,於是 <component :is="currentView"></component> 就會載入對應名稱的元件。

你可以在 JSFiddle 上試玩:

暫存動態元件的狀態

動態元件會在每次切換時被重新建立,如果你想將被切換掉的元件暫存在記憶體中不要被銷毀,可以使用 keep-alive 元素。
使用範例:
HTML<keep-alive>
  <component :is="currentView"></component>
</keep-alive>
你可以在元件中建立一個輸入框 <div>首頁<input type="text"></div> ,執行後在其中隨便輸入一些文字,接著點選其他按鈕,試試看有加 <keep-alive> 和沒加有什麼差別。
本文網址:http://blog.tonycube.com/2017/05/vuejs-9-2-component.html
Tony Blog 撰寫,請勿全文複製,轉載時請註明出處及連結,謝謝 😀

我要留言

留言小提醒:
1.回覆時間通常在晚上,如果太忙可能要等幾天。
2.請先瀏覽一下其他人的留言,也許有人問過同樣的問題。
3.程式碼請先將它編碼後再貼上。(線上編碼:http://bit.ly/1DL6yog)
4.文字請加上標點符號及斷行,難以閱讀者恕難回覆。
5.感謝您的留言,您的問題也可能幫助到其他有相同問題的人。