Vue-101-Day 12

|

Vue提供Component讓Vue Instance可以重複使用這些Component

Component範例:

Vue.component('button-counter', {
  data: function () {
    return {
      count: 0
    }
  },
  template: `<button v-on:click="count++">You clicked me {{ count }} times.</button>`
})

var vm14 = new Vue({
  el: '#app-14'
})

定義完Component後,再View中使用Component定義名稱 button-counter

<div id="app-14">
  <button-counter></button-counter>
</div>

重複使用Component

即定義所要的Component名稱及數量即可

<div id="app-14">
  <button-counter></button-counter>
  <button-counter></button-counter>
</div>

註:Component所使用的data屬性必須是Function,而不是Object

使用Props屬性傳遞資料給Component

可以註冊attribute在Component的Props裡面,一個Component裡面可以有多個props

範例:

Vue.component('blog-post', {
  props: ['title', 'author'],
  template: `<h3>{{ title }}, author is {{ author }}</h3>`
})

var vm15 = new Vue({
  el: '#app-15'
})

輸出結果

My first Vue, author is David
NBA Finals, author is Silver

可以透過資料數量來決定元件重複的數量

範例:此處的posts長度為3,所以需要重複三次post這個元件

Vue.component('post', {
  props: ['title'],
  template: `<h3>{{ title }}</h3>`
})
var vm16 = new Vue({
  el: '#app-16',
  data: {
    posts: [
      { id: 1, title: 'My journey with Vue' },
      { id: 2, title: 'Blogging with Vue' },
      { id: 3, title: 'Why Vue is so fun' }
    ]
  }
})
<div id="app-16">
  <post
    v-for="post in posts"
    v-bind:key="post.id"
    v-bind:title="post.title"
  ></post>
</div>

輸出結果

My journey with Vue
Blogging with Vue
Why Vue is so fun

通常一個元件裡面應該會顯示多個訊息,以上述來說,通常除了顯示title外,還會顯示content。

所以我們將元件調整如下

Vue.component('writings', {
  props: ['title', 'content'],
  template: `<h3>{{ title }}</h3> <h4>{{content}}</h4>`
})

乍似沒問題,但打開console會看到錯誤訊息如下

[Vue warn]: Error compiling template:
Component template should contain exactly one root element. ....

簡單來說只能有一個根元素,上述範例同時有兩個根元素 h3 及 h4,所以會出現問題

應該調整為

Vue.component('writings', {
  props: ['title', 'content'],
  template: `
  <div>
    <h3>{{ title }}</h3>
    <h4>{{ content }}</h4>
  </div>
`
})

var vm17 = new Vue({
  el: '#app-17',
  data: {
    posts: [
      { id: 2, title: 'Blogging with Vue', content: 'Hellooooooo' },
      { id: 3, title: 'Why Vue is so fun', content: 'Hiiiiiii' }
    ]
  }
})

隨著要顯示的資料越來越多,程式就必須做重構

只留下post這個attribute,顯示內容皆透過post取得

Vue.component('writings', {
  props: ['post'],
  template: `
  <div>
    <h3>{{ post.title }}</h3>
    <h4>{{ post.content }}</h4>
    <h5>{{ post.author }}</h5>
  </div>
`
})

var vm17 = new Vue({
  el: '#app-17',
  data: {
    posts: [
      {
        id: 2,
        title: 'Blogging with Vue',
        content: 'Hellooooooo',
        author: 'David'
      },
      { id: 3, title: 'Why Vue is so fun', content: 'Hiiiiiii', author: 'Mike' }
    ]
  }
})
<div id="app-17">
  <writings
    v-for="post in posts"
    v-bind:key="post.id"
    v-bind:post="post"
  ></writings>
</div>

監聽Component事件

有時候需要與使用者互動,因此必須監聽該Component。

實作一個範例,使用者點擊按鈕,文字會隨著加大

Vue.component('writings', {
  props: ['post'],
  template: `
  <div>
    <h3>{{ post.title }}</h3>
    <button>
      Enlarge Text
    </button>
    <div v-html="post.content"></div>
  </div>
`
})
var vm18 = new Vue({
  el: '#app-18',
  data: {
    posts: [
      {
        id: 1,
        title: 'Blogging with Vue'
      },
      { id: 2, title: 'Why Vue is so fun' }
    ],
    pageFontSize: 1
  }
})

View

這時候點擊按鈕並沒有觸發任何事件,所以要開始實作這個功能

先再Button註冊Click事件,點擊時觸發自定義的事件並利用$emit觸發事件

Vue.component('writings', {
  props: ['post'],
  template: `
  <div>
    <h3>{{ post.title }}</h3>
    <button v-on:click="$emit('enlargetext')">
      Enlarge text
    </button>
    <div v-html="post.content"></div>
  </div>
`
})

利用v-on接收自定義(enlargetext)的事件

<div id="app-18">
  <div :style="{fontSize: pageFontSize + 'em'}">
    <writings
      v-for="post in posts"
      v-bind:key="post.id"
      v-bind:post="post"
      v-on:enlargetext="pageFontSize += 0.1"
    ></writings>
  </div>
</div>

註:HTML attribute是沒有區分大小寫

伴隨事件發射一個值

可以再事件emit時,給定額外的value值

 Vue.component('writings', {
  props: ['post'],
  template: `
  <div>
    <h3>{{ post.title }}</h3>
    <button v-on:click="$emit('enlargetext', 0.1)">
      Enlarge text
    </button>
    <div v-html="post.content"></div>
  </div>
`
})

透過$event,接收被拋出的值(上述為0.1)

<div id="app-18">
  <div :style="{fontSize: pageFontSize + 'em'}">
    <writings
      v-for="post in posts"
      v-bind:key="post.id"
      v-bind:post="post"
      v-on:enlargetext="pageFontSize += $event"
    ></writings>
  </div>
</div>

或是將處理事件當作method處理

var vm18 = new Vue({
  el: '#app-18',
  data: {
    posts: [
      {
        id: 1,
        title: 'Blogging with Vue',
        content: 'Helloooooo'
      },
      { id: 2, title: 'Why Vue is so fun', content: 'Hiiiiiiii' }
    ],
    pageFontSize: 1
  },
  methods: {
    onEnlargeText: function (enlargeAmount) {
      this.pageFontSize += enlargeAmount
    }
  }
})
<div id="app-18">
  <div :style="{fontSize: pageFontSize + 'em'}">
    <writings
      v-for="post in posts"
      v-bind:key="post.id"
      v-bind:post="post"
      v-on:enlargetext="onEnlargeText"
    ></writings>
  </div>
</div>

在Component使用v-model

下述兩個HTML是相等的

<input v-model="searchText" />
<input
  v-bind:value="searchText"
  v-on:input="searchText = $event.target.value"
/>

用於Component上會是

Vue.component('custom-input', {
  props: ['value'],
  template: `
  <input
    v-bind:value="value"
    v-on:input="$emit('input', $event.target.value)"
  />
  `
})

var vm19 = new Vue({
  el: '#app-19',
  data: {
    searchText: ''
  }
})
<div id="app-19">
  <custom-input v-model="searchText"></custom-input>
</div>

參考資料:

Comments