Vue-組件詳解

檢察
原文站點,更多擴大內容及更佳瀏覽體驗!

組件詳解

組件與復用

Vue組件須要註冊后才能夠運用。註冊有全局註冊和部份註冊兩種體式格局。

全局註冊

Vue.component('my-component', {});

要在父實例中運用這個組件,必需要在實例建立前註冊,以後就能夠用<my-component></my-component>的情勢來運用組件。

Vue.component('my-component', {
    template: `<div>這是一個組件</div>`
});

template的DOM組織必需被一個元素包含,缺乏<div></div>會沒法襯着並報錯。

在Vue實例中,運用components選項能夠部份註冊組件,註冊后的組件只在該實例作用域下有效。

組件中也能夠運用components選項來註冊組件,使組件能夠嵌套。

var Child = {
    template: `<div>部份註冊組件的內容</div>`
};

new Vue({
    el: '#app',
    components: {
        'my-component': Child
    },
});

Vue組件的模板在某些狀況下會遭到HTML的限定,比方<table>內劃定只許可是<tr><td><th>等這些表格元素,所以在<table>內直接運用組件時無效的。這類狀況下,能夠運用特別的is屬性來掛載組件

<div id="app">
    <table>
        <tbody is="my-component"></tbody>
    </table>
</div>

Vue.component('my-component', {
    template: `<div>這裡是組件內容</div>`
});

罕見的限定元素另有<ul><ol><select>

除了template選項外,組件中還能夠像Vue實例那樣運用其他的選項,比方datacomputedmethods等。

然則在運用data時,data必需是函數,然後將數據return出去

JavaScript對象是援用關聯,假如return的對象援用了外部的一個對象,那這個對象就是同享的,任何一方修正都邑同步。

運用props通報數據

組件不僅要把模板的內容舉行復用,更重要的是組件間舉行通訊。

平常父組件的模板中包含子組件,父組件要正向地向子組件通報數據或參數,子組件吸收后依據參數的差別襯着差別的內容或許實行操縱。這個正向通報數據的歷程經由過程props來完成。

在組件中,運用選項props聲明須要從父級吸收的數據,props的值能夠是兩種,一種是字符串數組,一種是對象

<my-component message="來自父組件的數據"></my-component>

props: ['message'],
template: `<div>{{message}}</div>`,

props中聲明的數據與組件data函數中return的數據重要辨別就是props的數據來自父級,而data中的是組件本身的數據,作用域是組件本身,這兩種數據都能夠在模板template及盤算屬性computed和要領methods中運用。

由於HTML特徵不辨別大小寫,當運用DOM模板時,駝峰定名的props稱號要轉為短橫線支解定名。

<my-component warning-text="提醒信息"></my-component>

有時候,通報的數據並非直接寫死,而是來自父級的動態數據,這時候能夠運用指令v-bind動態綁定props的值,當父組件的數據變化時,也會通報子組件。

<div id="app">
    <input type="text" v-model="parentMessage">
    <my-component :message="parentMessage"></my-component>
</div>

props: ['message'],
template: `<div>{{message}}</div>`,

data: {
    parentMessage: ''
}

這裏用v-model綁定了父級的數據parentMessage,當經由過程輸入框恣意輸入時,子組件吸收到的props["message"]也會及時相應,並更新組件模板。

單向數據流

營業中會常常碰到兩種須要改變prop的狀況,一種是父組件通報初始值進來,子組件將它作為初始值保存起來,在本身的作用域下能夠隨便運用和修正。這類狀況能夠在組件data內再聲明一個數據,援用父組件的prop

<div id="app">
    <my-component :init-count="1"></my-component>
</div>

Vue.component('my-component', {
    props: ['initCount'],
    template: `<div>{{count}}</div>`,
    data() {
        return {
            count:this.initCount
        }
    }
});

組件中聲清楚明了數據count,它在組件初始化時會獵取來自父組件的initCount,以後就與之無關了,只用保護count,如許就能夠防止直接操縱initCount

另一種狀況就是prop作為須要被改變的原始值傳入,這類狀況用盤算屬性就能夠。

<div id="app">
    <my-component :width="100"></my-component>
</div>

Vue.component('my-component', {
    props: ['width'],
    template: `<div :style="style">組件內容</div>`,
    computed: {
        style: function () {
            return {
                width: this.width + 'px'
            }
        }
    }
});

由於用CSS通報寬度要帶單元(px),數值盤算平常不帶單元,所以統一在組件內運用盤算屬性。

在JavaScript中對象和數組時援用範例,指向同一個內存空間,所以
props是對象和數組時,在子組件內改變是會影響父組件。

數組考證

prop須要考證時,須要對象寫法。

平常當組件須要供應給他人運用時,引薦都舉行數據考證。比方某個數據必需是数字範例,假如傳入字符串,就會在控制台彈出正告。

Vue.component('my-component', {
    props: {
        // 必需是数字
        propA: Number,
        // 必需是字符串或数字範例
        propB: [String, Number],
        // 布爾值,假如沒有定義,默許值是true
        propC: {
            type: Boolean,
            default: true
        },
        // 数字,而且是必傳
        propD: {
            type: Number,
            default: true
        },
        // 假如是數組或對象,默許值必需是一個函數來返回
        propE: {
            type: Array,
            default: function () {
                return []
            }
        },
        // 自定義一個考證函數
        propF: {
            validator: function (value) {
                return value > 10
            }
        }
    }
});

考證的type範例能夠是:

  • String
  • Number
  • Boolean
  • Object
  • Array
  • Function

type也能夠是一個自定義組織器,運用instanceof檢測。

組件通訊

組件關聯可分為父組件通訊、兄弟組件通訊、跨級組件通訊。

自定義事宜

當子組件須要向父組件通報數據時,就要用到自定義事宜。

v-on除了監聽DOM事宜外,還能夠用於組件之間的自定義事宜。

JavaScript的設想形式——觀察者形式要領:

  • dispatchEvent
  • addEventListener

Vue組件的子組件用$emit()來觸發事宜,父組件用$on()來監聽子組件的事宜。

父組件也能夠直接在子組件的自定義標籤上運用v-on來監聽子組件觸發的自定義事宜。

<div id="app">
    <p>總數:{{total}}</p>
    <my-component
            @increase="handleGetTotal" @reduce="handleGetTotal"></my-component>
</div>

Vue.component('my-component', {
    template: `
        <div>
            <button @click="handleIncrease">+</button>
            <button @click="handlereduce">-</button>
        </div>
        `,
    data() {
        return {
            counter: 0
        }
    },
    methods: {
        handleIncrease: function () {
            this.counter++;
            this.$emit('increase', this.counter);
        },
        handlereduce: function () {
            this.counter--;
            this.$emit('reduce', this.counter)
        }
    }
});

new Vue({
    el: '#app',
    data: {
        total: 0
    },
    methods: {
        handleGetTotal: function (total) {
            this.total = total;
        }
    }
});

在改變組件的data "counter"后,經由過程$emit()再把它通報給父組件,父組件用@increase@reduce$emit()要領的第一個參數是自定義事宜的稱號。

除了用v-on在組件上監聽自定義事宜外,也能夠監聽DOM事宜,這時候能夠用.native修飾符示意監聽時一個原生事宜,監聽的是該組件的根元素。

<my-component @click:native="handleClick"></my-component>

運用v-model

Vue能夠在自定義組件上運用v-model指令。

<my-component v-model="total"></my-component>

組件$emit()的事宜名時特別的input,在運用組件的父級,並沒有在<my-component>上運用@input="handler",而是直接用了v-model綁定的一個數據total

<my-component @input="handleGetTotal"></my-component>

v-model還能夠用來建立自定義的表單輸入組件,舉行數據雙向綁定。

<div id="app">
    <p>總數:{{total}}</p>
    <my-component v-model="total"></my-component>
    <button @click="handleReduce">-</button>
</div>

Vue.component('my-component', {
    props: ['value'],
    template: `<input :value="value" @input="updateValue">`,
    methods: {
        updateValue: function () {
            this.$emit('input', event.target.value)
        }
    }
});

new Vue({
    el: '#app',
    data: {
        total: 10
    },
    methods: {
        handleReduce: function () {
            this.total--;
        }
    }
});

完成如許一個具有雙向綁定的v-model組件要滿足下面兩個請求:

  1. 吸收一個value屬性
  2. 在有新的value時觸發input事宜

非父子組件通訊

在現實營業中,除了父子組件通訊外,另有許多非父子組件通訊的場景,非父子組件平常有兩種,兄弟組件跨多級組件

Vue 1.x版本中,除了$emit()要領外,還供應了¥dispatch()$broadcast()

$dispatch()用於向上級派發事宜,只如果它的父級(一級或多級以上),都能夠在Vue實例的events選項內吸收。

此實例只在Vue 1.x版本中有效:

<div id="app">
    <p>{{message}}</p>
    <my-component></my-component>
</div>

Vue.component('my-component', {
    template: `<button @click="handleDispatch">派發事宜</button>`,
    methods: {
        handleDispatch: function () {
            this.$dispatch('on-message', '來自內部組件的數據')
        }
    }
});
new Vue({
    el: '#app',
    data: {
        message: ''
    },
    events: {
        'on-message': function (msg) {
            this.message = msg;
        }
    }
});

$broadcast()是由上級向下級播送事宜,用法完全一致,方向相反。

這兩種要領一旦發出事宜后,任何組件都能夠吸收到,就近準繩,而且會在第一次吸收到后住手冒泡,除非返回true

這些要領在Vue 2.x版本中已燒毀。

在Vue 2.x中,引薦任何一個空的Vue實例作為中心事宜總線(bus),也就是一个中介。

<div id="app">
    <p>{{message}}</p>
    <component-a></component-a>
</div>

var bus = new Vue();

Vue.component('component-a', {
    template: `<button @click="handleEvent">通報事宜</button>`,
    methods: {
        handleEvent: function () {
            bus.$emit('on-message', '來自組件component-a的內容')
        }
    }
});
var app = new Vue({
    el: '#app',
    data: {
        message: ''
    },
    mounted: function () {
        var _this = this;
        // 在實例初始化時,監聽來自bus實例的事宜
        bus.$on('on-message', function (msg) {
            _this.message = msg;
        })
    }
});

起首建立了一個名為bus的空的Vue實例;然後全局定義了組件component-a;末了建立了Vue實例app

app初始化時,也就是在生命周期mounted鈎子函數里監聽了來自bus的事宜on-message,而在組件component-a中,點擊按鈕後會經由過程bus把事宜on-message發出去。此時app就會吸收到來自bus的事宜,進而在回調里完成本身的營業邏輯。

這類要領奇妙而輕量地完成了任何組件間的通訊,包含父子、兄弟、跨級。

假如深切運用,能夠擴大bus實例,給它增加datamethodscomputed等選項,這些都是能夠公用的。

在營業中,尤其是協同開闢時異常有效,由於常常須要同享一些通用的信息,比方用戶登錄的昵稱、性別、郵箱等,另有效戶的受權token等。

只需在初始化時讓bus獵取一次,任何時候、任何組件就能夠從中直接運用,在單頁面富運用(SPA)中會很有用。

除了中心事宜總線bus外,另有兩種要領能夠完成組件間通訊:父鏈和子組件索引。

父鏈

在子組件中,運用this.$parent能夠直接接見該組件的父實例或組件,父組件也能夠經由過程this.$children接見它一切的子組件,而且能夠遞歸向上或向下無窮接見,直到根實例或最內層的組件。

<div id="app">
    <p>{{message}}</p>
    <component-a></component-a>
</div>

Vue.component('component-a', {
    template: `<button @click="handleEvent">經由過程父鏈直接修正數據</button>`,
    methods: {
        handleEvent: function () {
            this.$parent.message = '來自組件component-a的內容'
        }
    }
});
var app = new Vue({
    el: '#app',
    data: {
        message: ''
    }
});

只管Vue許可如許操縱,但在營業中,子組件應該盡量地防止依靠父組件的數據,更不應該去主動修正它的數據,由於如許使得父子組件緊耦合,只看父組件,很難明白父組件的狀況,由於它能夠被恣意組件修正,抱負狀況下,只要組件本身能修正它的狀況。

父子組件最好照樣經由過程props$emit()來通訊。

子組件索引

當子組件較多時,經由過程this.$children來遍歷出須要的一個組件實例是比較難題的,尤其是組件動態襯着時,它們的序列是不牢固的。

Vue供應了子組件索引的要領,用特別的屬性ref來為子組件指定一個索引稱號。

<div id="app">
    <button @click="handleRef">經由過程ref獵取子組件實例</button>
    <component-a ref="comA"></component-a>
</div>

Vue.component('component-a', {
    template: `<div>子組件</div>`,
    data() {
        return {
            message: '子組件內容'
        }
    },
});
var app = new Vue({
    el: '#app',
    methods: {
        handleRef: function () {
            // 經由過程$refs來接見指定的實例
            var msg = this.$refs.comA.message;
            console.log(msg);
        }
    }
});

在父組件模板中,子組件標籤上運用ref指定一個稱號,並在父組件內經由過程this.$refs來接見指定稱號的子組件。

$refs只在組件襯着完成后才添補,而且它黑白相應式的。它僅僅作為一個直接接見子組件的應急計劃,應該防止在模板或盤算屬性中運用
$refs

Vue 2.x將v-elv-ref合併成ref,Vue會自動去推斷是一般標籤照樣組件。

運用slot分發內容

當須要讓組件組合運用,夾雜父組件的內容與子組件的模板時,就會用到slot,這個歷程叫做內容分發

  • <app>組件不知道它的掛載點會有什麼內容。掛載點的內容是由<app>的父組件決議的。
  • <app>組件很能夠有它本身的模板。

props通報數據、events觸發事宜和slot內容分發就組成了Vue組件的3個API泉源,再龐雜的組件也是由這3部份組成。

作用域

父組件中的模板:

<child-component>
    {{message}}
</child-component>

這裏的message就是一個slot,然則它綁定的是父組件的數據,而不是組件<child-component>的數據。

父組件模板的內容是在父組件作用域內編譯,子組件模板的內容是在子組件作用域內編譯。

<div id="app">
    <child-component v-modle="showChild"></child-component>
</div>

Vue.component('child-component', {
    template: `<div>子組件1</div>`,
});
var app = new Vue({
    el: '#app',
    data: {
        showChild: true
    }
});

這裏的狀況showChild綁定的是父組件的數據。

在子組件上綁定數據:

<div id="app">
    <child-component></child-component>
</div>

Vue.component('child-component', {
    template: `<div v-model="showChild">子組件</div>`,
    data() {
        return {
            showChild: true
        }
    }
});
var app = new Vue({
    el: '#app',
});

因此,slot分發的內容,作用域是在父組件上

單個slot

在子組件內運用特別的<slot>元素就能夠為這個組件開啟一個slot(插槽),在父組件模板里,插進去在子組件標籤內的一切內容將替代子組件的<slot>標籤及它的內容。

<div id="app">
    <child-component>
        <p>分發的內容</p>
        <p>更多分發的內容</p>
    </child-component>
</div>

Vue.component('child-component', {
    template: `
    <div>
        <slot>
           <p>假如沒有父組件插進去內容,我將作為默許湧現。</p>
        </slot>
    </div>
            `,
});
var app = new Vue({
    el: '#app',
});

子組件child-component的模板內定義了一個<slot>元素,而且用一個<p>作為默許的內容,在父組件沒有運用slot時,會襯着這段默許的文本;假如寫入了slot,就會替代全部<slot>

子組件
<slot>內的備用內容,它的作用域是子組件本身。

簽字Slot

<slot>元素指定一個name后能夠分發多個內容,簽字slot能夠與單個slot共存。

<div id="app">
    <child-component>
        <h2 slot="header">題目</h2>
        <p>正文的內容</p>
        <p>更多正文的內容</p>
        <div slot="footer">底部信息</div>
    </child-component>
</div>

Vue.component('child-component', {
    template: `
<div class="container">
    <div class="header">
        <slot name="header"></slot>
    </div>
    <div class="main">
        <slot></slot>
    </div>
    <div class="footer">
        <slot name="footer"></slot>
    </div>
</div>    
    `,
});
var app = new Vue({
    el: '#app',
});

子組件內聲清楚明了3個<slot>元素,个中在<div class="main">內的<slot>沒有運用name特徵,它將作為默許slot湧現,父組件沒有運用slot特徵的元素與內容都將湧現在這裏。

假如沒有指定默許的匿名slot,父組件內過剩的內容都將被揚棄。

在組合運用組件時,內容分發API至關重要。

作用域插槽

作用域插槽是一種特別的slot,運用一個能夠復用的模板替代已襯着元素。

<div id="app">
    <child-component>
        <template scope="props">
            <p>來自父組件的內容</p>
            <p>{{props.msg}}</p>
        </template>
    </child-component>
</div>

Vue.component('child-component', {
    template: `
<div class="container">
    <slot msg="來自子組件的內容"></slot>
</div> 
    `,
});
var app = new Vue({
    el: '#app',
});

子組件的模板,在<slot>元素上有一個相似props通報數據給組件的寫法msg="xxx",將數據通報到插槽。

父組件中運用了<template>元素,而且具有一個scope="props"的特徵,這裏的props是一個暫時變量。

template內能夠經由過程暫時變量props接見來自子組件插槽的數據msg

作用域插槽更具代表性的用例是列表組件,許可組件自定義應該怎樣襯着列表每一項。

<div id="app">
    <my-list :book="books">
        <!--作用域插槽也能夠是簽字的Slot-->
        <template slot="book" scope="props">
            <li>{{props.bookName}}</li>
        </template>
    </my-list>
</div>

Vue.component('my-list', {
    props: {
        books: {
            type: Array,
            default: function () {
                return [];

            }
        }
    },
    template: `
<ul>
    <slot name="book" v-for="book in books" :book-name="book.name"></slot>
</ul>
    `,
});

子組件my-list吸收一個來自父級的prop數組books,而且將它在namebookslot上運用v-for指令輪迴,同時暴露一個變量bookName

作用域插槽的運用場景是既能夠復用子組件的slot,又能夠使slot內容不一致。

接見slot

Vue 2.x供應了用來接見被slot分發的內容的要領$slots

<div id="app">
    <child-component>
        <h2 slot="header">題目</h2>
        <p>正文的內容</p>
        <p>更多正文的內容</p>
        <div slot="footer">底部信息</div>
    </child-component>
</div>

Vue.component('child-component', {
    template: `
<div class="container">
    <div class="header">
        <slot name="header"></slot>
    </div>
    <div class="main">
        <slot></slot>
    </div>
    <div class="footer">
        <slot name="footer"></slot>
    </div>
</div>    
    `,
    mounted: function () {
        var header = this.$slots.header;
        var main = this.$slots.default;
        var footer = this.$slots.footer;
        console.log(footer);
        console.log(footer[0].elm.innerHTML);
    }
});
var app = new Vue({
    el: '#app',
});

經由過程$slots能夠接見某個簽字slot,this.$slots.default包含了一切沒有被包含在簽字slot中的節點。

組件高等用法

遞歸組件

給組件設置name選項,組件在它的模板內能夠遞歸地挪用本身。

<div id="app">
    <child-component :count="1"></child-component>
</div>

Vue.component('child-component', {
    name: 'child-component',
    props: {
        count: {
            type: Number,
            default: 1
        }
    },
    template: `
<div class="child">
    <child-component :count="count+1" v-if="count<3"></child-component>
</div>
    `,
});

組件遞歸運用能夠用來開闢一些具有未知層級關機的自力組件,比方級聯選擇器和樹形控件等。

內聯模板

組件的模板平常都是在template選項內定義的,Vue供應了一個內聯模板的功用,在運用組件時,給組件標籤運用inline-template特徵,組件就會把它的內容當作模板,而不是把它當內容分發,這讓模板更天真。

<div id="app">
    <child-component inline-template>
        <div>
            <h2>在父組件中定義子組件的模板</h2>
            <p>{{message}}</p>
            <p>{{msg}}</p>
        </div>
    </child-component>
</div>

Vue.component('child-component', {
    data() {
        return {
            msg: '在子組件中聲明的數據'
        }
    }
});
var app = new Vue({
    el: '#app',
    data: {
        message: '在父組件中聲明的數據'
    }
});

在父組件中聲明的數據message和子組件中聲明的數據msg,兩個都能夠襯着(假如同名,優先運用子組件的數據)。這是內聯模板的瑕玷,就是作用域比較難明白,假如不是異常特別的場景,發起不要隨意馬虎運用內聯模板

動態組件

Vue.js供應了一個特別的元素<component>用來動態地掛載差別的組件,運用is特徵來選摘要掛載的組件。

<div id="app">
    <button @click="handleChangeView('A')">切換到A</button>
    <button @click="handleChangeView('B')">切換到B</button>
    <button @click="handleChangeView('C')">切換到C</button>
    <component :is="currentView"></component>
</div>

var app = new Vue({
    el: '#app',
    data: {
        currentView: 'comA'
    },
    components: {
        comA: {
            template: `<div>組件A</div>`
        },
        comB: {
            template: `<div>組件B</div>`
        },
        comC: {
            template: `<div>組件C</div>`
        },
    },
    methods: {
        handleChangeView: function (component) {
            this.currentView = 'com' + component
        }
    }
});

能夠直接綁定在組件對象上:

<div id="app">
    <component :is="currentView"></component>
</div>

var Home = {
    template: `<p>Welcome home!</p>`
};

var app = new Vue({
    el: '#app',
    data: {
        currentView: Home
    }
});

異步組件

Vue.js許可將組件定義為一個工場函數,動態地剖析組件。

Vue.js只在組件須要襯着時觸發工場函數,而且把效果緩存起來,用於背面的再次襯着。

<div id="app">
    <child-component></child-component>
</div>

Vue.component('child-component', function (resolve, reject) {
    window.setTimeout(function () {
        resolve({
            template: `<div>我是異步襯着的!</div>`
        })
    }, 1000)
});

var app = new Vue({
    el: '#app',
});

工場函數吸收一個resolve回調,在收到從服務器下載的組件定義時挪用。也能夠挪用reject(reason)指導加載失利。

其他

$nextTick

異步更新行列

Vue在觀察到數據變化時並非直接更新DOM,而是開啟一個行列,並緩衝在同一個事宜輪迴中發作的一切數據變化。在緩衝時會去除反覆數據,從而防止不必要的盤算和DOM操縱。然後,在一下個事宜輪迴tick中,Vue革新行列並實行現實(已去重的)事情。

Vue會依據當前瀏覽器環境優先運用原生的Promise.thenMutationObserver,假如都不支撐,就會採納setTimeout替代。

$nextTick就是用來肯定什麼時候DOM更新已完成

<div id="app">
    <div id="div" v-if="showDiv">這是一段文本</div>
    <button @click="getText">獵取div內容</button>
</div>

var app = new Vue({
    el: '#app',
    data: {
        showDiv: false
    },
    methods: {
        getText: function () {
            this.showDiv = true;
            this.$nextTick(function () {
                var text = document.getElementById('div');
                console.log(text.innerHTML);
            })
        }
    }
});

X-Templates

Vue供應了另一種定義模板的體式格局,在<script>標籤中運用text/x-template範例,而且指定一個id,將這個id賦給template

<div id="app">
    <my-component></my-component>
    <script type="text/x-template" id="my-component">
        <div>這是組件的內容</div>
    </script>
</div>

Vue.component('my-component', {
    template: `#my-component`,
});
var app = new Vue({
    el: '#app',
});

手動掛載實例

在一些異常特別的狀況下,須要動態地建立Vue實例,Vue供應了Vue.extend$mount兩個要領來手動掛載一個實例。

Vue.extend是基本Vue組織器,建立一個“子類”,參數是一個包含組件選項的對象。

假如Vue實例在實例化時沒有收到el選項,它就處於“未掛載”狀況,沒有關聯的DOM元素。能夠運用$mount手動地掛載一個未掛載的實例。這個要領返回實例本身,因此能夠鏈式挪用其他實例要領。

<div id="app"></div>

var MyComponent = Vue.extend({
    template: `<div>Hello {{name}}</div>`,
    data() {
        return {
            name: 'Andy'
        }
    }
});
new MyComponent().$mount('#app');

除了以上寫法外,另有兩種寫法:

new MyComponent().$mount("#app");

new MyComponent({
    el: '#app'
})

手動掛載實例(組件)是一種比較極度的高等用法,在營業中險些用不到,只在開闢一些龐雜的自力組件時能夠會運用。

    原文作者:whjin
    原文地址: https://segmentfault.com/a/1190000015199363
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞