Vue.js元件基礎
通過事件向父級元件傳送訊息
在我們開發元件時,它的一些功能有可能要求我們與父元件進行溝通。例如我們可能會引入一個可訪問性的功能來放大博文的字號,同時讓頁面的其他部分保持預設的字號。
這裡主要通過的是$emit方法,該方法可以傳送要呼叫的函式名稱,這個類似於事件通知系統。觸發該事件的元件呼叫該方法,需要被觸發的元件,則註冊該方法。
既然是向父元件傳送訊息,自然是要改變父元件裡的屬性了。
首先在子元件裡要傳送訊息:
Vue.component('blog-postt', { props:['post'], template: ` <div class="blog-post"> <h3>{{ post.title }}</h3> <button v-on:click="$emit('enlarge-text')"> Enlarge text </button> <div v-html="post.content"></div> </div> ` })
還要改變父元件裡的屬性,那麼父元件裡就要先定義屬性:
new Vue({ el: '#blog-posts-events-demo', data: { posts:[ {id: 0, text: 'Vegetables', title: 'Vegetables', content: 'Vegetables'}, {id: 1, text: 'Cheese', title: 'Cheese', content: 'Cheese'}, {id: 2, text: 'Whatever else humans are supposed to eat', title: 'Whatever else humans are supposed to eat', content: 'Whatever else humans are supposed to eat'} ], postFontSize: 1 } })
這裡我們選用postFontSize,通過子元件裡的按鈕控制父元件中的字號大小。
看看我們註冊該函式enlarge-text的地方:
<div id="blog-posts-events-demo"> <div v-bind:style="{fontSize: postFontSize + 'em' }"> <blog-postt v-for="post in posts" v-bind:key="post.id" v-bind:post="post" v-on:enlarge-text="postFontSize += 0.1" ></blog-postt> </div> </div>
在子元件建立的時候,制定好傳送訊息的函式,在呼叫子元件的時候,註冊該訊息對應的處理,在該處理裡我們改變父元件的屬性。
使用事件丟擲一個值
有時候使用事件了丟擲一個值是十分有用的。例如我們想讓<blog-post>元件決定它的文字要放大多少,這時使用$emit的第二個引數來提供這個值。
依然是先在子元件裡觸發函式:
Vue.component('blog-post', {
props:['post'],
template:`
<div class="blog-post">
<h3>{{ post.title }}</h3>
<button v-on:click="$emit('enlarge-text', 0.1)">
Enlarge text
</button>
<div v-html="post.content"></div>
</div>
`
})
只不過此時emit的第二個值並不為空,被我們設定為父元件字號要增大的增量0.1。
依然使用定義在父元件裡中的屬性postFontSize:
new Vue({
el: '#blog-posts-events-demo',
data: {
posts:[
{id: 0, text: 'Vegetables', title: 'Vegetables', content: 'Vegetables'},
{id: 1, text: 'Cheese', title: 'Cheese', content: 'Cheese'},
{id: 2, text: 'Whatever else humans are supposed to eat', title: 'Whatever else humans are supposed to eat', content: 'Whatever else humans are supposed to eat'}
],
postFontSize: 1
}
})
現在看看在子元件呼叫的時候如何接收這個引數:
<div id="blog-posts-events-demo">
<div v-bind:style="{fontSize: postFontSize + 'em'}">
<blog-post
v-for="post in posts"
v-bind:key="post.id"
v-bind:post="post"
v-on:enlarge-text="postFontSize += $event"
></blog-post>
</div>
</div>
沒錯大同小異,就是在註冊的函式末尾直接使用$event就可以獲得觸發函式時傳送的值了。
如果該事件的的處理函式是一個方法:
<div v-bind:style="{fontSize: postFontSize + 'em'">
<blog-post
v-on:enlarge-text="onEnlargeText"
></blog-post>
</div>
那麼就要在這個方法裡接收這個引數了。
new Vue({
el: '#blog-posts-events-demo',
data: {
posts:[
{id: 0, text: 'Vegetables', title: 'Vegetables', content: 'Vegetables'},
{id: 1, text: 'Cheese', title: 'Cheese', content: 'Cheese'},
{id: 2, text: 'Whatever else humans are supposed to eat', title: 'Whatever else humans are supposed to eat', content: 'Whatever else humans are supposed to eat'}
],
postFontSize: 1
},
methods: {
onEnlargeText: function(enlargeAmount) {
this.postFontSize += enlargeAmount
}
}
})
在元件上使用v-model
自定義元件也可以用於建立支援v-model的自定義輸入元件。記住:
<input v-model="searchText">
等價於
<input v-bind:value="searchText"
v-bind:input="searchText = $event.target.value"
>
當用在元件上時,v-modle會是這樣:
<custom-input
v-bind:value="searchText"
v-on:input="searchText = $event"
></custom-input>
為了讓它正常工作,這個元件內的<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
Everything is ok.
></custom-input>
通過插槽分發內容
和Html一樣我們通常需要向一個元件傳遞內容。例如
<alert-box>
It's time to have lunch.
</alert-box>
幸好Vue自定義 <slot> 讓這一切變得十分容易。
Vue.component('alert-box', {
template: `
<div class="demo-alert-box">
<strong>Error!</strong>
<slot></slot>
</div>
`
})
動態元件
有的時候,在不同元件之間,進行動態切換是非常有用的。比如在一個多標籤的介面裡:
上述的內容可以通過Vue的<component>元素加一個 is 特性實現:
<!-- 元件會在`currentTabComponent`改變時改變 -->
<component v-bind:is="currentTabComponent"></component>
在上述示例中,currentTabComponent可以包括:
- 已註冊元件的名字
- 一個元件的選項物件
先說第一種情況,已註冊元件的名字,既然是不同元件,自然元件有多個,先定義元件:
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>'
})
新建Vue例項:
new Vue({
el: '#dynamic-component-tab',
data: {
currentTab: 'Home',
tabs:['Home', 'Posts', 'Archive']
},
computed: {
currentTabComponent: function() {
return 'tab' + this.currentTab.toLowerCase()
}
}
})
在Html中引用:
<div id="dynamic-component-demo">
<button
v-for="tab in tabs"
v-bind:key="tab"
v-bind:class="['tab-button', {archive: currentTab === tab}]"
v-on:click="currentTab = tab"
></button>
<component
v-bind:is="currentTabComponent"
class="tab"
></component>
</div>
第二種情況,一個元件的選項物件。
先看一下JS程式碼:
var tabs = [
{
name: 'Home',
component: {
template: '<div>Home component</div>'
}
},
{
name: 'Posts',
component: {
template: '<div>Posts component</div>'
}
},
{
name: 'Archive',
component: {
template: '<div>Archive component</div>'
}
}
]
new Vue({
el: '#dynamic-component-demo1',
data: {
tabs: tabs,
currentTab: tabs[0]
}
})
再看一下Html中的呼叫:
<div id="dynamic-component-demo1">
<button
v-for="tab in tabs"
v-bind:key="tab.name"
v-bind:class="['tab-button', { archive: currentTab.name === tab.name }]"
v-on:click="currentTab = tab"
>{{ tab.name }}</button>
<component
v-bind:is="currentTab.component"
class="tab"
></component>
</div>
解析DOM模板時的注意事項:
有些 HTML 元素,諸如 <ul>
、<ol>
、<table>
和 <select>
,對於哪些元素可以出現在其內部是有嚴格限制的。而有些元素,諸如 <li>
、<tr>
和 <option>
,只能出現在其它某些特定的元素內部。
這會導致我們使用這些有約束條件的元素時遇到一些問題。例如:
<table> <blog-post-row></blog-post-row> </table> |
這個自定義元件 <blog-post-row>
會被作為無效的內容提升到外部,並導致最終渲染結果出錯。幸好這個特殊的 is
特性給了我們一個變通的辦法:
<table> <tr is="blog-post-row"></tr> </table> |
需要注意的是如果我們從以下來源使用模板的話,這條限制是不存在的:
- 字串 (例如:
template: '...'
) - 單檔案元件 (
.vue
) <script type="text/x-template">
到這裡,你需要了解的解析 DOM 模板時的注意事項——實際上也是 Vue 的全部必要內容,大概就是這些了。恭喜你!接下來還有很多東西要去學習,不過首先,我們推薦你先休息一下,試用一下 Vue,自己隨意做些好玩的東西。
如果你感覺已經掌握了這些知識,我們推薦你再回來把完整的元件指南,包括側邊欄中元件深入章節的所有頁面讀完。