1單文件組件
前面在vue.js破冰系列2中提到HTML模板有4中方式定義刃滓,分別是:在掛載點內(nèi)定義模板计呈、在template選項中定義模板茁彭、以x-template方式類型模板尉间,以及使用單文件組件(single-file components)的方式贪薪。這里我們在講解單文件組件。
單文件組件顧名思義霍弹,一個文件就是一個組件台丛,文件的擴展名為.vue
防嗡。它的好處有很多他嫡,比如支持語法高亮近顷,css樣式等。最重要的是譬挚,有效的支持前端工程化以及關(guān)注點分離。前端工程化一般使用webpack等構(gòu)建工具,當(dāng)我們項目越來越大時统舀,使用單文件組件方便代碼的組織和維護(關(guān)注點分離)。
一個單文件組件.vue
包含3個頂級語言塊<template>
毛甲、<scirpt>
叮叹、<style>
,vue通過外部loader將這3個語言塊解析為一個組件選項對象闲勺。
一個單文件中只能包含一個template和一個script語言塊,但是可以包含多個style語言塊,在template標(biāo)簽中只能存在一個根節(jié)點谚中。格式如下:
//xxx.vue
<template></template>
<scirpt></scirpt>
<style></style>
2Props數(shù)據(jù)傳遞
2.1 props的定義
在注冊組件時,使用props選項定義組件的特征比搭,父組件通過這些特征向子組件傳遞數(shù)據(jù)抄囚,props可以接收數(shù)組和對象作為參數(shù),數(shù)組中包含string類型的值锅劝,如果要對屬性做更多的配置隅津,則需要使用object類型。
<template>
<div>
<!--props的引用-->
<h4>{{message}}</h4>
</div>
</template>
<script>
export default{
//數(shù)組的方式定義props
//props:["message"],
//對象的方式定義props
props:{
message:String,
}
}
</script>
props的數(shù)組寫法相對簡單,只需要定義屬性名即可协怒。如果要對屬性做一些約定則需要用到對象寫法孕暇,對象的鍵為屬性名隧哮,值可以是類型或一個屬性參數(shù)對象鉴竭。當(dāng)屬性值為類型時矢洲,表示對該屬性做類型約束袁滥,如果是屬性參數(shù)對象,則可以做更高級的約束:
<script>
export default{
props:{
屬性名:{
type:'屬性類型,String|Number|Boolean|Array|Object|Data|Function|Symbol|[可能的類型集合]',
default:'默認(rèn)值彼硫,Object和Array類型的prop需要使用函數(shù)的方式(工廠函數(shù))返回,原理同組件中的data使用函數(shù)返回一樣',
required:'是否為必填項掖肋,Boolean',
validator:'驗證函數(shù)纫溃,返回Boolean值疗锐,如果返回false口芍,在控制臺會發(fā)出警告'
}
}
}
</script>
可以看到props使用對象語法時,可以定義屬性的類型,默認(rèn)值踊赠,是否為必須已經(jīng)驗證信息等。
Vue.component('my-component', {
props: {
// 基礎(chǔ)的類型檢查 (`null` 和 `undefined` 會通過任何類型驗證)
propA: Number,
// 多個可能的類型
propB: [String, Number],
// 必填的字符串
propC: {
type: String,
required: true
},
// 帶有默認(rèn)值的數(shù)字
propD: {
type: Number,
default: 100
},
// 帶有默認(rèn)值的對象
propE: {
type: Object,
// 對象或數(shù)組默認(rèn)值必須從一個工廠函數(shù)獲取
default: function () {
return { message: 'hello' }
}
},
// 自定義驗證函數(shù)
propF: {
validator: function (value) {
// 這個值必須匹配下列字符串中的一個
return ['success', 'warning', 'danger'].indexOf(value) !== -1
}
}
}
})
2.2 props的使用
html標(biāo)簽是不區(qū)分大小寫的烫堤,如果porps使用Pascal命名,Vue會將其轉(zhuǎn)換為kebab-case形式(我們在上一章提到過Pascal和kebab)立倍。
//子組件welcome.vue
<tempalte>
<div>
<span>你好 {{name}},上次登錄時間:{{lastLoginTime}}</span>
</div>
</tempalte>
<script>
export default{
props:['name','lastLoginTime']
}
</script>
父組件調(diào)用:
<tempalte>
<div>
<!-- 使用kebab形式調(diào)用子組件的屬性 -->
<welcome :name="name" :last-login-time="time" />
</div>
</tempalte>
<script>
//導(dǎo)入
import Welcome from 'welcome.vue'
export default{
//局部注冊
components:{
Welcome,
},
data(){
return {
name:'張三',
time:'2019-09-17',
}
}
}
</script>
2.3 單向數(shù)據(jù)流
子組件定義props君珠,父組件向子組件傳遞數(shù)據(jù)材部,當(dāng)父組件中的數(shù)據(jù)改變后摩窃,子組件會重新渲染账阻,數(shù)據(jù)從父向子流動叫做數(shù)據(jù)的單向流動。Vue不允許在子組件中改變props的值,如果強行修改挎扰,Vue會在控制臺中發(fā)出警告信息。
//子組件 child-component.vue
<template>
<div>
<h4>子組件中的title:{{title}}</h4>
<button @click="changePropsHandle">修改Props</button>
</div>
</template>
<script>
export default{
props:{
title:String,
},
methods:{
changePropsHandle(){
this.title="子組件修改的title"
}
}
}
</script>
<template>
<div>
<h4>父組件中的title:{{title}}</h4>
<child :title="title" />
</div>
</template>
<script>
import Child from 'child-component.vue'
export default{
components:{
Child
},
data(){
return {
title:"我是父組件中的title"
}
}
}
</script>
這個例子在點擊子組件中的按鈕是會在控制臺中出現(xiàn)警告信息,原因是Vue中不允許直接修改props中的數(shù)據(jù)。如果要修改props煞抬,可以用data和computed替待革答。
//子組件 child-component.vue
<script>
export default{
props:{
title:String,
},
//title作為初始值保存在localTilte中
data(){
return{
loaclTitle:this.title,
}
},
computed:{
//將title轉(zhuǎn)換為另一個字符串曙强,
computeTilte:function(){
return "Hello"+this.title;
}
},
}
</script>
注意:當(dāng)props的類型為Object或Array時途茫,在子組件中修改props的項時,會影響到父組件囊卜,原因可以參考其他語言的引用傳遞和值傳遞。
3組件間通信
組件之間的關(guān)系可以簡單的分為父子關(guān)系和非父子關(guān)系栅组。父子關(guān)系的組件,父組件直接調(diào)用子組件刃麸,比如使用子組件的props傳值給組件司浪,或者使用子組件的slot插槽將內(nèi)容分發(fā)到子組件。
3.1自定義事件
上面說了,子組件中不允許修改父組件傳遞的數(shù)據(jù)智政。有些情況下认罩,子組件需要修改父組件傳遞來的值续捂,這時,可以使用自定義事件通知父組件牙瓢,由父組件修改其數(shù)據(jù),間接的的達(dá)到子組件修改props矾克。
我們在vue.js破冰系列2中提到v-on事件監(jiān)聽的內(nèi)部指令,過程大致是這樣的胁附,父組件在子組件的標(biāo)簽中使用v-on監(jiān)聽事件,子組件中使用實例的內(nèi)部方法$emit
觸發(fā)事件控妻,$emit
方法的定義如下:
vm.$emit('evnetName',[...args])
vm表示的是實例,在具體的組件中郎哭,可以使用this指代。$emit
的第1個參數(shù)為事件名稱夸研,他是一個字符串,vue不能自動轉(zhuǎn)換其大小寫陈惰,然而畦徘,使用v-on監(jiān)聽事件是在DOM標(biāo)簽中抬闯,我們知道DOM標(biāo)簽是不區(qū)分大小寫的,DOM會將全部標(biāo)簽轉(zhuǎn)換為小寫溶握,更進一步,要事件的觸發(fā)睡榆,$emit
中事件名必須完全匹配v-on指令中的參數(shù)(事件名),所以vue建議在$emit
和v-on
定義的事件名使用kebab樣式塘揣,或者也可以使用全小寫的方式。
<div id="app">
<!--DOM 會將MyEvent轉(zhuǎn)換為小寫myevnet -->
<child @MyEvent="myEventHandler"></child>
<!-- 推薦使用kebab形式的事件名 -->
<child @my-event="myEventHandler" />
</div>
<!--子組件-->
<script>
export default{
methods:{
btnClicked(){
//該emit無效亲铡,因為v-on指令監(jiān)聽的事件名為myevent,而這里觸發(fā)的是MyEvent事件
this.$emit('MyEvent');
//該emit有效
this.$emit('my-event');
}
}
}
</script>
$emit
的第2個參數(shù)為不定參數(shù)奖蔓,表示的是該事件對應(yīng)處理函數(shù)的實參讹堤。關(guān)于自定義事件可以參考vue.js破冰系列2中的v-on
和v-model
章節(jié)。
3.2中央事件總線
上面我們講了父子組件之間的通信疑务,他們是逐級傳遞的梗醇,也就是說父組件要和孫子組件通信,需要經(jīng)過子組件婴削,其他的還有兄弟組件之間的通信需要通過父組件。這種非父子組件之間的通信Vue推薦使用中央事件總線(bus)唉俗,原理是這樣的配椭,創(chuàng)建一個vue的空實例雹姊,使用vue實例方法$emit
來觸發(fā)事件,使用vue實例方法$on
來監(jiān)聽事件敦姻。
//創(chuàng)建一個vue的空實例歧杏,注意不能使用一個Object實例,因為我們要用到Vue實例的$emit和$on方法
var bus = new Vue();
//組件A注冊監(jiān)聽bus上事件
export ComA={
created(){
//注冊監(jiān)聽bus上的事件
bus.$on('my-evnet',myEvnetHandler)
},
methods:{
myEvnetHandler(arg1){
//事件處理邏輯
},
}
}
//組件B
export ComB = {
methods:{
clicked(){
//觸發(fā)bus上的my-event事件
bus.$emit('my-event','Hello World!')
}
}
}
上面的例子演示了組件A和組件B之間的通信旺入。