關(guān)于如何學(xué)習(xí)Vue,Vue的締造者尤雨溪前輩曾經(jīng)在知乎發(fā)表過(guò)一篇新手向:Vue 2.0 的建議學(xué)習(xí)順序。
關(guān)于Vue其他知識(shí)介紹:篇幅一
5. 組件
- 創(chuàng)建(注冊(cè))組件
// 注冊(cè)(全局注冊(cè))
Vue.component('my-component', {
template: '<div>A custom component!</div>'
})
組件在注冊(cè)之后,便可以在父實(shí)例的模塊中以自定義元素 <my-component></my-component> 的形式使用。要確保在初始化根實(shí)例之前注冊(cè)了組件:
<div id="example">
<my-component></my-component>
</div>
//這里注冊(cè)
// 創(chuàng)建根實(shí)例
new Vue({
el: '#example'
})
- data
通過(guò)Vue構(gòu)造器創(chuàng)建的組件中,data
屬性必須是函數(shù)。
Vue.component('my-app',{
template: '<span>{{message}}</span>',
data: function(){
return {
message: 'hello'
}
}
})
- Props
子組件通過(guò)顯式地用props選項(xiàng)聲明它期待獲得的數(shù)據(jù)。 - 單向數(shù)據(jù)流
prop 是單向綁定的:當(dāng)父組件的屬性變化時(shí),將傳導(dǎo)給子組件,但是不會(huì)反過(guò)來(lái)。每次父組件更新時(shí),子組件的所有 prop 都會(huì)更新為最新值。這意味著你不應(yīng)該在子組件內(nèi)部改變 prop。
為什么我們會(huì)有修改 prop 中數(shù)據(jù)的沖動(dòng)呢?通常是這兩種原因:
(1)prop 作為初始值傳入后,子組件想把它當(dāng)作局部數(shù)據(jù)來(lái)用;
(2)prop 作為初始值傳入,由子組件處理成其它數(shù)據(jù)輸出。
對(duì)這兩種原因,正確的應(yīng)對(duì)方式是:
定義一個(gè)局部變量,并用 prop 的值初始化它:
props: ['initialCounter'],
data: function () {
return { counter: this.initialCounter }
}
定義一個(gè)計(jì)算屬性,處理 prop 的值并返回。
props: ['size'],
computed: {
normalizedSize: function () {
return this.size.trim().toLowerCase()
}
}
- Prop驗(yàn)證
組件傳入的props
可以進(jìn)行數(shù)據(jù)驗(yàn)證。要指定驗(yàn)證規(guī)格,需要用對(duì)象的形式,而不能用字符串?dāng)?shù)組。如:
Vue.component('my-app',{
props: {
propA: Number,//基本類型,null表示任何類型都行
propB: [String,Number],//多種類型
propC: {
type: String,
required: true //必須傳,且時(shí)字符串
},
propD: {
type: Number,
default: 100 // 數(shù)字,有默認(rèn)值
},
propE: {
type: Object,
default: function () {
return { message: 'hello' }
} // 數(shù)組/對(duì)象的默認(rèn)值應(yīng)當(dāng)由一個(gè)工廠函數(shù)返回
},
propF: {
validator: function (value) {
return value > 10 // 自定義驗(yàn)證函數(shù)
}
}
}
})
注意:props
會(huì)在組件實(shí)例創(chuàng)建之前進(jìn)行校驗(yàn),所以在 default
或 validator
函數(shù)里,諸如data
、computed
或 methods
等實(shí)例屬性還無(wú)法使用。
- 自定義事件
子組件通過(guò)自定義事件跟父組件通信。
5.1 使用v-on
綁定自定義事件
每個(gè)Vue實(shí)例都實(shí)現(xiàn)了事件接口,即:
- 使用
$on(eventName)
監(jiān)聽(tīng)事件 - 使用
$emit(eventName)
觸發(fā)事件
另外,父組件可以在使用子組件的地方直接用v-on
來(lái)監(jiān)聽(tīng)子組件觸發(fā)的事件。
注意:不能用 $on 偵聽(tīng)子組件釋放的事件,而必須在模板里直接用 v-on 綁定。例如:
<template>
<div id = "app">
{{ count }}
<child v-on:add="addcount"></child>
</div>
</template>
Vue.component('child',{
template: '<button @click="increment">父組件count +1={{number}}</button>',
data: function(){
return {
number : 0
},
method : {
increment: function(){
this.number++
$.emit(add)
}
}
}
})
new Vue({
el: '#app',
data: {
count : 0
},
methods: {
addcount: function(){
this.count++
}
}
})
以上代碼表示,點(diǎn)擊子組件按鈕時(shí)候,會(huì)觸發(fā)父組件的addcount()
事件
5.2 .sync修飾符
.sync作為一個(gè)編譯語(yǔ)法糖存在,他會(huì)被擴(kuò)展為一個(gè)自動(dòng)更新父組件屬性的v-on
偵聽(tīng)器,如:
<comp :foo.sync="bar"></comp>
擴(kuò)展為:
<comp :foo="bar" @update:foo="val => var =val"></comp>
當(dāng)組件需要需要更新foo
的值時(shí),它需要顯示地觸發(fā)一個(gè)更新事件:
this.$emit('update:foo',newValue)
5.3 非父子組件通信
有時(shí)候兩個(gè)組件也需要通信(非父子關(guān)系),簡(jiǎn)單的場(chǎng)景下,可以使用一個(gè)空的Vue實(shí)例作為中央事件總線:
var bus = new Vue()
bus.$emit('id-selected',1) //觸發(fā)組件A中的事件
bus.$on('id-selected',function(id){})//在組件B創(chuàng)建的鉤子中監(jiān)聽(tīng)事件
注意:復(fù)雜情況下,應(yīng)該考慮專門的狀態(tài)管理模式
- 使用插槽分發(fā)內(nèi)容
為了讓組件可以結(jié)合,我們需要一種方式來(lái)混合組租間的內(nèi)容與子組件自己的模板,這個(gè)過(guò)程被稱為內(nèi)容分發(fā),Vue使用<slot>
作為原始內(nèi)容的插槽。如下:
//app-layout 組件模板如下
<div class="container">
<header>
<slot name="header"></slot> //具名插槽
</header>
<main>
<slot></slot> //默認(rèn)插槽
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
//父組件模板:
<app-layout>
<h1 slot="header">這里可能是一個(gè)頁(yè)面標(biāo)題</h1>
<p>主要內(nèi)容的一個(gè)段落。</p>
<p>另一個(gè)主要段落。</p>
<p slot="footer">這里有一些聯(lián)系信息</p>
</app-layout>
渲染結(jié)果為:
<div class="container">
<header>
<h1>這里可能是一個(gè)頁(yè)面標(biāo)題</h1>
</header>
<main>
<p>主要內(nèi)容的一個(gè)段落。</p>
<p>另一個(gè)主要段落。</p>
</main>
<footer>
<p>這里有一些聯(lián)系信息</p>
</footer>
</div>
6.1 編譯作用域
分發(fā)內(nèi)容實(shí)在父作用域內(nèi)編譯。
6.2 作用域插槽
作用域插槽是一種特殊類型的插槽,用作一個(gè)替換已渲染元素的 (能被傳遞數(shù)據(jù)的) 可重用模板。
在子組件中,只需將數(shù)據(jù)傳遞到插槽,就像你將 props 傳遞給組件一樣:
<div class="child">
<slot text="hello from child"></slot>
</div>
在父級(jí)中,具有特殊屬性 scope 的 <template> 元素必須存在,表示它是作用域插槽的模板。scope 的值對(duì)應(yīng)一個(gè)臨時(shí)變量名,此變量接收從子組件中傳遞的 props 對(duì)象:
<div class="parent">
<child>
<template scope="props">
<span>hello from parent</span>
<span>{{ props.text }}</span>
</template>
</child>
</div>
如果我們渲染以上結(jié)果,得到的輸出會(huì)是:
<div class="parent">
<div class="child">
<span>hello from parent</span>
<span>hello from child</span>
</div>
</div>
作用域插槽更具代表性的用例是列表組件,允許組件自定義應(yīng)該如何渲染列表每一項(xiàng):
<my-awesome-list :items="items">
<!-- 作用域插槽也可以是具名的 -->
<template slot="item" scope="props">
<li class="my-fancy-item">{{ props.text }}</li>
</template>
</my-awesome-list>
列表組件的模板:
<ul>
<slot name="item"
v-for="item in items"
:text="item.text">
<!-- 這里寫入備用內(nèi)容 -->
</slot>
</ul>
- 子組件索引
有時(shí)候需要在js中直接訪問(wèn)子組件,為此可以使用ref
為子組件制定一個(gè)索引 ID,如下:
<div id="parent">
<user-profile ref="profile"></user-profile>
</div>
var parent = new Vue({ el: '#parent' })
// 訪問(wèn)子組件
var child = parent.$refs.profile
注意:$refs
只在組件渲染完成后才填充,并且它是非響應(yīng)式的。它僅僅作為一個(gè)直接訪問(wèn)子組件的應(yīng)急方案——應(yīng)當(dāng)避免在模板或計(jì)算屬性中使用 $refs