1. 程式人生 > >Vue.js元件基礎

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>

需要注意的是如果我們從以下來源使用模板的話,這條限制是不存在

到這裡,你需要了解的解析 DOM 模板時的注意事項——實際上也是 Vue 的全部必要內容,大概就是這些了。恭喜你!接下來還有很多東西要去學習,不過首先,我們推薦你先休息一下,試用一下 Vue,自己隨意做些好玩的東西。

如果你感覺已經掌握了這些知識,我們推薦你再回來把完整的元件指南,包括側邊欄中元件深入章節的所有頁面讀完。