檢察
原文站點,更多擴大內容及更佳瀏覽體驗!
組件詳解
組件與復用
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實例那樣運用其他的選項,比方data
、computed
、methods
等。
然則在運用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
組件要滿足下面兩個請求:
- 吸收一個
value
屬性 - 在有新的
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
實例,給它增加data
、methods
、computed
等選項,這些都是能夠公用的。
在營業中,尤其是協同開闢時異常有效,由於常常須要同享一些通用的信息,比方用戶登錄的昵稱、性別、郵箱等,另有效戶的受權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-el
和v-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
,而且將它在name
為book
的slot
上運用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.then
和MutationObserver
,假如都不支撐,就會採納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'
})
手動掛載實例(組件)是一種比較極度的高等用法,在營業中險些用不到,只在開闢一些龐雜的自力組件時能夠會運用。