Vue入門之Vuex實戰
引言
Vue組件化做的確實非常徹底,它獨有的vue單文件組件也是做的非常有特色。組件化的同時帶來的是:組件之間的數據共享和通信的難題。 尤其Vue組件設計的就是,父組件通過子組件的prop進行傳遞數據,而且數據傳遞是單向
的。也就是說:父組件可以把數據傳遞給子組件,但是 反之則不同。如下圖所示:
單向數據流動
單方向的數據流動帶來了非常簡潔和清晰的數據流,純展示性或者獨立性較強的模塊的開發確實非常方便和省事。 但是復雜的頁面邏輯,組件之間的數據共享處理就會需要通過事件總線的方式解決或者使用Vue的Vuex框架了
子組件通知父組件數據更新:事件方式的實現
子組件可以在子組件內觸發事件,然後在父容器中添加子組件時綁定父容器的方法為事件響應方法的方式.如下圖所示
- 使用 v-on 綁定自定義事件
-
//每個 Vue 實例都實現了事件接口(Events interface),即: //使用 $on(eventName) 監聽事件 //使用 $emit(eventName) 觸發事件
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Vue入門之event message</title> <!-- 新 Bootstrap 核心 CSS 文件 --> <link rel="stylesheet" href="http://cdn.bootcss.com/bootstrap/3.3.0/css/bootstrap.min.css"> <!-- 可選的Bootstrap主題文件(一般不用引入) --> <link rel="stylesheet" href="http://cdn.bootcss.com/bootstrap/3.3.0/css/bootstrap-theme.min.css"> <!-- jQuery文件。務必在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>推薦次數:{{ voteCount }}</p> <hr> <!--綁定兩個自定義事件,當組件內部觸發了事件後,會自定調用父容器綁定的methods的方法,達到了子容器向父容器數據進行通信同步的方法--> <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">發送消息</button> </div> `, data:function () { return { txtMsg: "" } }, methods: { voteArticle: function () { // 觸發事件,vote this.$emit(‘vote‘) }, sendMsg: function () { // 觸發事件,sendmsg,並 this.$emit(‘sendmsg‘, this.txtMsg) } } }) var app = new Vue({ el: ‘#app‘, data: { voteCount: 0, msg: [] }, methods: { voteAction: function() { // 事件觸發後,會直接執行此方法 this.voteCount += 1 }, sendMsgAction: function (item) { this.msg.push(item) } } }); </script> </body> </html>
如果非父子組件怎麽通過事件進行同步數據,或者同步消息呢?Vue中的事件觸發和監聽都是跟一個具體的Vue實例掛鉤。 所以在不同的Vue實例中想進行事件的統一跟蹤和觸發,那就需要一個公共的Vue實例,這個實例就是公共的事件對象。
參考下面做的一個購物車的案例的代碼:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Vue入門之event message</title> <!-- 新 Bootstrap 核心 CSS 文件 --> <link rel="stylesheet" href="http://cdn.bootcss.com/bootstrap/3.3.0/css/bootstrap.min.css"> <!-- 可選的Bootstrap主題文件(一般不用引入) --> <link rel="stylesheet" href="http://cdn.bootcss.com/bootstrap/3.3.0/css/bootstrap-theme.min.css"> <!-- jQuery文件。務必在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>商品編號</th> <th>商品名</th> <th>數量</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>商品編號</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: ‘鱷魚‘ }, { 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>
Vuex解決復雜單頁面應用
上面的方式只能解決一些簡單的頁面中的組件的通信問題,但是如果是復雜的單頁面應用就需要使用更強大的Vuex來幫我們進行狀態的統一管理和同步。
當第一次接觸Vuex的時候,眼前一亮,之前經過Redux之後,被它繁瑣的使用令我痛苦不已,雖然思路很清晰,其實完全可以設計的更簡單和高效。 當我接觸到Vuex之後,發現這就是我想要的。的確簡潔就是一種藝術。
其實本質上,Vuex就是一個大的EventBus對象的升級版本,相當於一個特定的倉庫,所有數據都在統一的倉庫中,進行統一的管理。
幾個核心的概念:
- State: Vuex倉庫中的數據。
- Getter: 類似於Vue實例中的計算屬性,Getter就是普通的獲取state包裝函數。
- Mutations: Vuex 的 store 中的狀態的唯一方法是提交 mutation。Vuex 中的 mutations 非常類似於事件:每個 mutation 都有一個字符串的 事件類型 (type) 和 一個 回調函數 (handler)。
- Action: action可以觸發Mutations,不能直接改變state。
看下面一張圖了解一下Vuex整體的數據流動:
Vuex實例demo
可能前面的圖和概念都太多了,先看一個例子,簡單了解一下Vuex中的倉庫的數據 怎麽整合到 Vue的實例中去。
創建Vuexdemo的項目
Vue入門之Vuex實戰