9.1. 引言
Vue組件化做的確實(shí)非常徹底篱蝇,它獨(dú)有的vue單文件組件也是做的非常有特色。組件化的同時(shí)帶來(lái)的是:組件之間的數(shù)據(jù)共享和通信的難題徽曲。 尤其Vue組件設(shè)計(jì)的就是零截,父組件通過(guò)子組件的prop進(jìn)行傳遞數(shù)據(jù),而且數(shù)據(jù)傳遞是單向
的秃臣。也就是說(shuō):父組件可以把數(shù)據(jù)傳遞給子組件涧衙,但是 反之則不同。如下圖所示:
9.2. 單向數(shù)據(jù)流動(dòng)
單方向的數(shù)據(jù)流動(dòng)帶來(lái)了非常簡(jiǎn)潔和清晰的數(shù)據(jù)流奥此,純展示性或者獨(dú)立性較強(qiáng)的模塊的開(kāi)發(fā)確實(shí)非常方便和省事弧哎。 但是復(fù)雜的頁(yè)面邏輯,組件之間的數(shù)據(jù)共享處理就會(huì)需要通過(guò)事件總線的方式解決或者使用Vue的Vuex框架了稚虎。
9.3. 子組件通知父組件數(shù)據(jù)更新:事件方式的實(shí)現(xiàn)
子組件可以在子組件內(nèi)觸發(fā)事件撤嫩,然后在父容器中添加子組件時(shí)綁定父容器的方法為事件響應(yīng)方法的方式.如下圖所示:
- 使用 v-on 綁定自定義事件
每個(gè) Vue 實(shí)例都實(shí)現(xiàn)了事件接口(Events interface),即:
使用 $on(eventName) 監(jiān)聽(tīng)事件
使用 $emit(eventName) 觸發(fā)事件
參考代碼案例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Vue入門(mén)之event message</title>
<!-- 新 Bootstrap 核心 CSS 文件 -->
<link rel="stylesheet" >
<!-- 可選的Bootstrap主題文件(一般不用引入) -->
<link rel="stylesheet" >
<!-- jQuery文件蠢终。務(wù)必在bootstrap.min.js 之前引入 -->
<script src="http://cdn.bootcss.com/jquery/1.11.1/jquery.min.js"></script>
<!-- 最新的 Bootstrap 核心 JavaScript 文件 -->
<script src="http://cdn.bootcss.com/bootstrap/3.3.0/js/bootstrap.min.js"></script>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
</head>
<body>
<div id="app">
<p>推薦次數(shù):{{ voteCount }}</p>
<hr>
<!--綁定兩個(gè)自定義事件序攘,當(dāng)組件內(nèi)部觸發(fā)了事件后,會(huì)自定調(diào)用父容器綁定的methods的方法寻拂,達(dá)到了子容器向父容器數(shù)據(jù)進(jìn)行通信同步的方法-->
<vote-btn v-on:vote="voteAction" v-on:sendmsg="sendMsgAction"></vote-btn>
<hr>
<ul class="list-group">
<li v-for="o in msg" class="list-group-item">{{o}}</li>
</ul>
</div>
<script>
Vue.component('vote-btn', {
template: `
<div>
<button class="btn btn-success" v-on:click="voteArticle">推薦</button>
<hr/>
<input type="text" v-model="txtMsg" />
<button v-on:click="sendMsg" class="btn btn-success">發(fā)送消息</button>
</div>
`,
data: function () {
return {
txtMsg: ""
}
},
methods: {
voteArticle: function () {
// 觸發(fā)事件程奠,vote
this.$emit('vote')
},
sendMsg: function () {
// 觸發(fā)事件,sendmsg祭钉,并
this.$emit('sendmsg', this.txtMsg)
}
}
})
var app = new Vue({
el: '#app',
data: {
voteCount: 0,
msg: []
},
methods: {
voteAction: function() { // 事件觸發(fā)后瞄沙,會(huì)直接執(zhí)行此方法
this.voteCount += 1
},
sendMsgAction: function (item) {
this.msg.push(item)
}
}
});
</script>
</body>
</html>
9.4. 事件總線方式解決非父子組件數(shù)據(jù)同步
如果非父子組件怎么通過(guò)事件進(jìn)行同步數(shù)據(jù),或者同步消息呢朴皆?Vue中的事件觸發(fā)和監(jiān)聽(tīng)都是跟一個(gè)具體的Vue實(shí)例掛鉤帕识。 所以在不同的Vue實(shí)例中想進(jìn)行事件的統(tǒng)一跟蹤和觸發(fā),那就需要一個(gè)公共的Vue實(shí)例遂铡,這個(gè)實(shí)例就是公共的事件對(duì)象肮疗。
參考下面做的一個(gè)購(gòu)物車(chē)的案例的代碼:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Vue入門(mén)之event message</title>
<!-- 新 Bootstrap 核心 CSS 文件 -->
<link rel="stylesheet" >
<!-- 可選的Bootstrap主題文件(一般不用引入) -->
<link rel="stylesheet" >
<!-- jQuery文件。務(wù)必在bootstrap.min.js 之前引入 -->
<script src="http://cdn.bootcss.com/jquery/1.11.1/jquery.min.js"></script>
<!-- 最新的 Bootstrap 核心 JavaScript 文件 -->
<script src="http://cdn.bootcss.com/bootstrap/3.3.0/js/bootstrap.min.js"></script>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
</head>
<body>
<div id="app">
<product-list :products="products" v-on:addpro="addToCarts"> </product-list>
<hr>
<cart :cart-products="carts"> </cart>
</div>
<script>
var eventBus = new Vue()
Vue.component('cart', {
template: `
<table class="table table-borderd table-striped table-hover">
<thead>
<tr>
<th>商品編號(hào)</th>
<th>商品名</th>
<th>數(shù)量</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for="item in cartProducts">
<td>{{ item.id }}</td>
<td>{{ item.name }}</td>
<td>
{{ item.count }}
</td>
<td>
<button type="button" @click="removeCarts(item)" class="btn btn-success"><i class="glyphicon glyphicon-remove"></i></button>
</td>
</tr>
</tbody>
</table>
`,
data: function () {
return {
}
},
methods: {
removeCarts: function (item) {
eventBus.$emit('remo', item)
}
},
props: ['cartProducts']
})
Vue.component('product-list', {
template: `
<table class="table table-borderd table-striped table-hover">
<thead>
<tr>
<th>商品編號(hào)</th>
<th>商品名</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for="item in products">
<td>{{ item.id }}</td>
<td>{{ item.name }}</td>
<td>
<button type="button" v-on:click="addToCarts(item)" class="btn btn-success"><i class="glyphicon glyphicon-shopping-cart"></i></button>
</td>
</tr>
</tbody>
</table>
`,
data: function () {
return {
}
},
methods: {
addToCarts: function (item) {
this.$emit('addpro', item)
}
},
props: ['products'],
})
var app = new Vue({
el: '#app',
data: {
products: [
{ id: '1', name: '鱷魚(yú)' },
{ id: '2', name: '蛇' },
{ id: '3', name: '兔子' },
{ id: '4', name: '驢' },
{ id: '5', name: '孔雀' }
],
carts: []
},
methods: {
addToCarts: function (item) {
var isExist = false
for(var i=0; i<this.carts.length; i++) {
if( item.id === this.carts[i].id ) {
item.count = this.carts[i].count + 1
Vue.set(this.carts, i, item)
isExist = true
}
}
!isExist && (item.count = 1, this.carts.push(item))
},
removeCarts: function (item) {
for(var i =0; i<this.carts.length; i++) {
if( item.id === this.carts[i].id) {
this.carts.splice(i,1)
}
}
}
},
mounted: function () {
self = this;
eventBus.$on('remo', function (item) {
self.removeCarts(item)
})
}
});
</script>
</body>
</html>
9.5. Vuex解決復(fù)雜單頁(yè)面應(yīng)用
上面的方式只能解決一些簡(jiǎn)單的頁(yè)面中的組件的通信問(wèn)題扒接,但是如果是復(fù)雜的單頁(yè)面應(yīng)用就需要使用更強(qiáng)大的Vuex來(lái)幫我們進(jìn)行狀態(tài)的統(tǒng)一管理和同步伪货。
當(dāng)?shù)谝淮谓佑|Vuex的時(shí)候,眼前一亮钾怔,之前經(jīng)過(guò)Redux之后碱呼,被它繁瑣的使用令我痛苦不已,雖然思路很清晰宗侦,其實(shí)完全可以設(shè)計(jì)的更簡(jiǎn)單和高效愚臀。 當(dāng)我接觸到Vuex之后,發(fā)現(xiàn)這就是我想要的矾利。的確簡(jiǎn)潔就是一種藝術(shù)姑裂。
其實(shí)本質(zhì)上馋袜,Vuex就是一個(gè)大的EventBus對(duì)象的升級(jí)版本,相當(dāng)于一個(gè)特定的倉(cāng)庫(kù)舶斧,所有數(shù)據(jù)都在統(tǒng)一的倉(cāng)庫(kù)中欣鳖,進(jìn)行統(tǒng)一的管理。
幾個(gè)核心的概念:
- State: Vuex倉(cāng)庫(kù)中的數(shù)據(jù)茴厉。
- Getter: 類(lèi)似于Vue實(shí)例中的計(jì)算屬性泽台,Getter就是普通的獲取state包裝函數(shù)。
- Mutations: Vuex 的 store 中的狀態(tài)的唯一方法是提交 mutation矾缓。Vuex 中的 mutations 非常類(lèi)似于事件:每個(gè) mutation 都有一個(gè)字符串的 事件類(lèi)型 (type) 和 一個(gè) 回調(diào)函數(shù) (handler)怀酷。
- Action: action可以觸發(fā)Mutations,不能直接改變state而账。
看下面一張圖了解一下Vuex整體的數(shù)據(jù)流動(dòng):
9.6. Vuex實(shí)例demo
可能前面的圖和概念都太多了胰坟,先看一個(gè)例子因篇,簡(jiǎn)單了解一下Vuex中的倉(cāng)庫(kù)的數(shù)據(jù) 怎么整合到 Vue的實(shí)例中去泞辐。
創(chuàng)建Vuexdemo的項(xiàng)目
# 通過(guò)vue-cli創(chuàng)建vuexdemo的項(xiàng)目,注意首先cd到你的存放項(xiàng)目代碼的目錄
vue init webpack vuexdemo
# 過(guò)程中竞滓,會(huì)有幾個(gè)選項(xiàng)你可以選擇輸入Y或者n來(lái)開(kāi)啟或者關(guān)閉某些選項(xiàng)咐吼。
# 創(chuàng)建完成后,就可以通過(guò)以下命令商佑,進(jìn)行初始化和安裝相關(guān)的依賴項(xiàng)了锯茄。
cd vuexdemo
npm install
npm run dev
# 然后安裝 vuex
npm i vuex -S
聯(lián)系老馬
對(duì)應(yīng)視頻地址:https://chuanke.baidu.com/s5508922.html
老馬qq: 515154084
老馬微信:請(qǐng)掃碼