1. 程式人生 > >Vue:學習筆記(六)-元件基礎

Vue:學習筆記(六)-元件基礎

提醒

原帖完整收藏於IT老兵驛站,並會不斷更新。

前言

最近在工作中頻繁遇到元件,所以就沒按照順序,先總結元件這一章節的內容了,將來再做調整和修改。

(2018-10-24注:這一章粗讀了兩遍,感覺下面有好些內容都沒有理解,有一些難度,看不明白就先修改一會兒,先乾點別的。)

正文

基本示例

這是一個例子:

// 定義一個名為 button-counter 的新元件
Vue.component('button-counter', {
  data: function () {
    return {
      count: 0
    }
  },
  template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>'
})

在我看來,元件就是模板、資料,加上對於資料處理的邏輯,這些顯示和邏輯,基本是固定的,所以以元件的形式固化出來。(感覺,這話一說出來,立刻就清楚了)

<div id="components-demo">
  <button-counter></button-counter>
</div>
new Vue({ el: '#components-demo' })

id等於“components-demo”是Vue物件的根元素,在它下面建立元件。

元件的複用

這裡是將把元件應該理解成類,每一個例項是物件,物件之間是沒有關係的。

data 必須是一個函式

只有將data實現為一個函式,才能實現上面的物件之間沒有關係的複用。

元件的組織

這裡有兩種元件的註冊型別:全域性註冊和區域性註冊。至此,我們的元件都只是通過 Vue.component 全域性註冊的:

Vue.component('my-component-name', {
  // ... options ...
})

通過 Prop 向子元件傳遞資料

Prop 是你可以在元件上註冊的一些自定義特性。當一個值傳遞給一個 prop 特性的時候,它就變成了那個元件例項的一個屬性。為了給博文元件傳遞一個標題,我們可以用一個 props 選項將其包含在該元件可接受的 prop 列表中:

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

感覺這個props僅僅是用來約束,如果沒有這個約束,所有在元件的屬性中送過來的屬性都會被接受。

單個根元素

這一章節講了涉及多個元素的元件是如何組織的。
例如:

<h3>{{ title }}</h3>
<div v-html="content"></div>

如果模板涉及了多個元素,那麼需要將這些元素放在一個根元素內,上面這個模板就涉及到兩個元素,這個時候需要在外面再包裹一個元素:

<div class="blog-post">
  <h3>{{ title }}</h3>
  <div v-html="content"></div>
</div>

下面的例子涉及到如何設計傳遞的引數。

<blog-post
  v-for="post in posts"
  v-bind:key="post.id"
  v-bind:title="post.title"
  v-bind:content="post.content"
  v-bind:publishedAt="post.publishedAt"
  v-bind:comments="post.comments"
</blog-post>

合理的設計應該是:

<blog-post
  v-for="post in posts"
  v-bind:key="post.id"
  v-bind:post="post"
></blog-post>
Vue.component('blog-post', {
  props: ['post'],
  template: `
    <div class="blog-post">
      <h3>{{ post.title }}</h3>
      <div v-html="post.content"></div>
    </div>
  `
})

通過事件向父級元件傳送訊息

這段沒想明白應該怎麼去總結,完全照搬過來沒有什麼意義。

<button v-on:click="$emit('enlarge-text')">
  Enlarge text
</button>

這個是孩子元件(位於父元件之內),可以使用$emit向父元件傳送一個自定義的訊息。

<blog-post
  ...
  v-on:enlarge-text="postFontSize += 0.1"
></blog-post>

父元件使用v-on去監聽這個事件。

使用事件丟擲一個值

接著上面的例子,如果想給父元件傳送訊息的同時,傳遞引數,需要這樣:
孩子元件:

<button v-on:click="$emit('enlarge-text', 0.1)">
  Enlarge text
</button>

父元件:

<blog-post
  ...
  v-on:enlarge-text="postFontSize += $event"
></blog-post>

這裡第二個引數,可以是一個值,或者是一個方法

methods: {
  onEnlargeText: function (enlargeAmount) {
    this.postFontSize += enlargeAmount
  }
}

好像不能是物件,這個還要確認一下。

在元件上使用 v-model

有一點含糊了,prop和v-model在這裡有什麼區別呢?這個部分講的感覺又有點不清楚。

<input v-model="searchText">

等價於

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

用在自定義的元件上(上面是標準的HTML元素),則是

<custom-input
  v-bind:value="searchText"
  v-on:input="searchText = $event"
></custom-input>

和上面大體接近,只是最後的$event有區別。

為了讓它正常工作,這個元件內的 <input> 必須:
將其 value 特性繫結到一個名叫 value 的 prop 上
在其 input 事件被觸發時,將新的值通過自定義的 input 事件丟擲
寫成程式碼之後是這樣的:

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

現在 v-model 就應該可以在這個元件上完美地工作起來了:
<custom-input v-model="searchText"></custom-input>

意思是:

<custom-input
  v-bind:value="searchText"
  v-on:input="searchText = $event"
></custom-input>

這麼寫和

<custom-input v-model="searchText"></custom-input>

這麼寫是一個意思,元件裡面都要這麼寫:

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

對event進行了一次轉發。

通過插槽分發內容

這裡講的是如何把元素的內容傳遞給元件,叫做分發內容,使用<slot>這樣的關鍵字來接收。

<alert-box>
  Something bad happened.
</alert-box>

上面是一個自定義元件,給這個元件直接傳遞內容,會得到不正確的結果,需要有能接收這個內容的東西,這個就叫插槽。這個地方感覺講的不清楚,自己嘗試了一下。

如上圖,這裡並沒有顯示出內容來,沒有用於接收的東西。

改為:

Vue.component('alert-box', {
  template: `
    <div class="demo-alert-box">
      <strong>Error!</strong>
      <slot></slot>
    </div>
  `
})

則:

這樣才顯示出來。

動態元件

這段又沒看懂。

<div id="dynamic-component-demo" class="demo">
  <button v-for="tab in tabs" v-bind:key="tab" class="dynamic-component-demo-tab-button" v-bind:class="{ 'dynamic-component-demo-tab-button-active': tab === currentTab }" 
  v-on:click="currentTab = tab">
    {{ tab }}
  </button>
  <component v-bind:is="currentTabComponent" class="dynamic-component-demo-tab"></component>
</div>
<script>
Vue.component('tab-home', { template: '<div>Home component</div>' })
Vue.component('tab-posts', { template: '<div>Posts component</div>' })
Vue.component('tab-archive', { template: '<div>Archive component</div>' })
new Vue({
  el: '#dynamic-component-demo',
  data: {
    currentTab: 'Home',
    tabs: ['Home', 'Posts', 'Archive']
  },
  computed: {
    currentTabComponent: function () {
      return 'tab-' + this.currentTab.toLowerCase()
    }
  }
})
</script>
<style>
.dynamic-component-demo-tab-button {
  padding: 6px 10px;
  border-top-left-radius: 3px;
  border-top-right-radius: 3px;
  border: 1px solid #ccc;
  cursor: pointer;
  background: #f0f0f0;
  margin-bottom: -1px;
  margin-right: -1px;
}
.dynamic-component-demo-tab-button:hover {
  background: #e0e0e0;
}
.dynamic-component-demo-tab-button-active {
  background: #e0e0e0;
}
.dynamic-component-demo-tab {
  border: 1px solid #ccc;
  padding: 10px;
}
</style>

解析 DOM 模板時的注意事項

這段和上面那段有關聯,但是好像也是沒寫清楚,繼續等待深度理解。

總結

斷斷續續,花了三天,終於大體看明白了(花了很多時間看這個slot,也把後面的關於slot的看了一遍,基本梳理清楚),感覺這一章節是Vue的核心內容,還需要不斷完善這篇筆記。

參考

https://cn.vuejs.org/v2/guide/components.html