代碼下載
Vuex 概述
組件之間共享數據的方式:
- 父組件向子組件傳值,是以屬性的形式綁定值到子組件(v-bind),然后子組件用屬性props接收。
- 子組件向父組件傳值,子組件用 $emit() 自定義事件,父組件用v-on監聽子組件的事件。
- 兄弟組件的傳值,通過事件中心傳遞數據,提供事件中心
var hub = new Vue()
,傳遞數據方通過一個事件觸發hub.$emit(方法名,傳遞的數據)
,接收數據方通過在mounted(){}
鉤子中 觸發hub.$on(方法名, 傳遞的數據)
。
Vuex是實現組件全局狀態(數據)管理的一種機制,可以方便的實現組件之間的數據共享。使用Vuex管理數據的好處:
- 能夠在vuex中集中管理共享的數據,便于開發和后期進行維護
- 能夠高效的實現組件之間的數據共享,提高開發效率
- 存儲在vuex中的數據是響應式的,當數據發生改變時,頁面中的數據也會同步更新
一般情況下,只有組件之間共享的數據,才有必要存儲到 vuex 中;對于組件中的私有數據,依舊存儲在組件自身的 data 中即可。
Vuex 簡單使用
1、安裝 vuex 依賴包
npm install vuex --save
2、導入 vuex 包
import Vuex from 'vuex'
Vue.use(Vuex)
3、創建 store 對象
const store = new Vuex.Store({// state 中存放的就是全局共享的數據state: { count: 0 }
})
4、 將 store 對象掛載到 vue 實例中
new Vue({el: '#app',render: h => h(app),// 將創建的共享數據對象,掛載到 Vue 實例中// 所有的組件,就可以直接從 store 中獲取全局的數據了store
})
在使用 vue 腳手架創建項目時,可以在功能選項中直接開啟使用 Vuex 的開關。
Vuex 的核心概念
Vuex 中的主要核心概念如下:State
,Mutation
,Action
,Getter
。
State
State 提供唯一的公共數據源,所有共享的數據都要統一放到 Store 的 State 中進行存儲。
// 創建store數據源,提供唯一公共數據
const store = new Vuex.Store({state: { count: 0 }
})
組件訪問 State 中數據的第一種方式:this.$store.state.全局數據名稱
組件訪問 State 中數據的第二種方式:
1、從 vuex 中按需導入 mapState 函數:
import { mapState } from 'vuex'
2、通過剛才導入的 mapState 函數,將當前組件需要的全局數據,映射為當前組件的 computed 計算屬性:
computed: {...mapState(['count'])
}
Mutation
Mutation 用于變更 Store中 的數據。
注意:只能通過 mutation 變更 Store 數據,不可以直接操作 Store 中的數據。通過這種方式雖然操作起來稍微繁瑣一些,但是可以集中監控所有數據的變化。
定義 Mutation,也可以在觸發 mutations 時傳遞參數:
export default new Vuex.Store({state: {count: 0},mutations: {add(state) {state.count++},addN(state, step) {state.count += step}}
})
組件觸發 mutations 的第一種方式:this.$store.commit('add')
、 this.$store.commit('add', 3)
。
組件觸發 mutations 的第二種方式:
1、從 vuex 中按需導入 mapMutations 函數
import { mapMutations } from 'vuex'
2、通過剛才導入的 mapMutations 函數,將需要的 mutations 函數,映射為當前組件的 methods 方法
methods: {...mapMutations(['add', 'addN'])
}
Action
Action 用于處理異步任務。如果通過異步操作變更數據,必須通過 Action,而不能使用 Mutation,但是在 Action 中還是要通過觸發 Mutation 的方式間接變更數據。
定義 Action,也可以在觸發 actions 異步任務時攜帶參數:
actions: {addAsync(context) {setTimeout(() => {context.commit('add')}, 1000);},addNAsync(context, step) {setTimeout(() => {context.commit('addN', step)}, 1000);}}
觸發 actions 的第一種方式:this.$store.dispatch('addSsync')
、this.$store.dispatch('addAsync', 3)
。
觸發 actions 的第二種方式:
1、從 vuex 中按需導入 mapActions 函數
import { mapActions } from 'vuex'
2、通過剛才導入的 mapActions 函數,將需要的 actions 函數,映射為當前組件的 methods 方法:
methods: {...mapActions(['addASync', 'addNASync'])
}
Getter
Getter 用于對 Store 中的數據進行加工處理形成新的數據,類似 Vue 的計算屬性。Store 中數據發生變化,Getter 的數據也會跟著變化。
定義 Getter:
export default new Vuex.Store({state: {count: 0},getters: {showNum(state) {return '當前最新數量是:' + state.count}}
}
使用 getters 的第一種方式:this.$store.getters.showNum
。
使用 getters 的第二種方式:
1、從 vuex 中按需導入 mapGetters 函數
import { mapGetters } from 'vuex'
2、通過剛才導入的 mapGetters 函數,將需要的 getters 函數,映射為當前組件的 computed 計算屬性:
computed: {...mapGetters(['showNum'])
}
示例
1、在 store > index.js 中創建 store 對象,并定義相應的 state、mutations、actions、getters:
import Vue from 'vue'
import Vuex from 'vuex'Vue.use(Vuex)export default new Vuex.Store({state: {count: 0},getters: {showNum(state) {return '當前最新數量是:' + state.count}},mutations: {add(state) {state.count++},addN(state, step) {state.count += step},sub(state) {state.count--},subN(state, step) {state.count -= step}},actions: {addAsync(context) {setTimeout(() => {context.commit('add')}, 1000);},addNAsync(context, step) {setTimeout(() => {context.commit('addN', step)}, 1000);},subAsync(context) {setTimeout(() => {context.commit('sub')}, 1000);},subNAsync(context, step) {setTimeout(() => {context.commit('subN', step)}, 1000);}},modules: {}
})
2、在 components > addition.vue 文件中,運用第一種方法使用 state、mutations、actions、getters:
<template><div><h3>計算結果:{{$store.state.count}}</h3><button @click="btnHandle1">+1</button><div><input type="number" placeholder="請輸入數值" v-model.number="addNum"><button @click="btnHandleN">+{{addNum}}</button></div><button @click="btnHandle1Async">+1 async</button><div><input type="number" placeholder="請輸入數值" v-model.number="addNumAsync"><button @click="btnHandleNAsync">+{{addNumAsync}}</button></div><h3>{{$store.getters.showNum}}</h3></div>
</template><script>
export default {data() {return {addNum: 2,addNumAsync: 3}},methods: {btnHandle1() {this.$store.commit('add')},btnHandleN() {this.$store.commit('addN', this.addNum)},btnHandle1Async() {this.$store.dispatch('addAsync')},btnHandleNAsync() {this.$store.dispatch('addNAsync', this.addNumAsync)}}
}
</script>
3、在 components > subtraction.vue 文件中,運用第二種方法使用 state、mutations、actions、getters:
<template><div><h3>計算結果:{{count}}</h3><button @click="btnHandle1">-1</button><div><input type="number" placeholder="請輸入數值" v-model.number="subNum"><button @click="btnHandleN">-{{subNum}}</button></div><button @click="subAsync">-1 async</button><div><input type="number" placeholder="請輸入數值" v-model.number="subNumAsync"><button @click="subNAsync(subNumAsync)">-{{subNumAsync}} async</button></div><h3>{{showNum}}</h3></div>
</template><script>
import { mapState, mapMutations, mapActions, mapGetters } from 'vuex';
export default {data() {return {subNum: 2,subNumAsync: 3}},computed: {...mapState(['count']),...mapGetters(['showNum'])},methods: {...mapMutations(['sub', 'subN']),btnHandle1() {this.sub()}, btnHandleN() {this.subN(this.subNum)},...mapActions(['subAsync', 'subNAsync'])}
}
</script>
Vuex 案例
項目初始化
1、通過 vue ui 命令打開可視化面板,創建新項目(略,這里使用的是和上面示例同一個項目)。
2、安裝依賴包:npm install axios
、npm install ant-design-vue@1.7.8
,因為這里使用的 vue2.x,所以 ant-design-vue
版本使用1.7.8。
注意:需要在 package.json 文件中將 eslint-plugin-vue
版本改為7.0.0 "eslint-plugin-vue": "^7.0.0"
,否則會與 ant-design-vue
有版本沖突。
打開 main.js 導入并配置相應的組件庫:
// 1. 導入 ant-design-vue 組件庫
import Antd from 'ant-design-vue'
// 2. 導入組件庫的樣式表
import 'ant-design-vue/dist/antd.css'
// 3. 安裝組件庫
Vue.use(Antd)
3、實現 Todos 基本布局,在 components 文件夾中新建 toDoList.vue 文件,并實現布局代碼:
<template><div id="app"><a-input placeholder="請輸入任務" class="my_ipt" /><a-button type="primary">添加事項</a-button><a-list bordered :dataSource="list" class="dt_list"><a-list-item slot="renderItem" slot-scope="item"><!-- 復選框 --><a-checkbox>{{item.info}}</a-checkbox><!-- 刪除鏈接 --><a slot="actions">刪除</a></a-list-item><!-- footer區域 --><div slot="footer" class="footer"><!-- 未完成的任務個數 --><span>0條剩余</span><!-- 操作按鈕 --><a-button-group><a-button type="primary">全部</a-button><a-button>未完成</a-button><a-button>已完成</a-button></a-button-group><!-- 把已經完成的任務清空 --><a>清除已完成</a></div></a-list></div>
</template><script>
export default {name: 'app',data() {return {list: [{id: 0,info: 'Racing car sprays burning fuel into crowd.',done: false},{ id: 1, info: 'Japanese princess to wed commoner.', done: false },{id: 2,info: 'Australian walks 100km after outback crash.',done: false},{ id: 3, info: 'Man charged over missing wedding girl.', done: false },{ id: 4, info: 'Los Angeles battles huge wildfires.', done: false }]}}
}
</script><style scoped>
#app {padding: 10px;
}.my_ipt {width: 500px;margin-right: 10px;
}.dt_list {width: 500px;margin-top: 10px;
}.footer {display: flex;justify-content: space-between;align-items: center;
}
</style>
功能實現
動態加載任務列表數據
1、打開public文件夾,創建一個list.json文件,填充數據:
[{"id": 0,"info": "Racing car sprays burning fuel into crowd.","done": false},{"id": 1,"info": "Japanese princess to wed commoner.","done": false},{"id": 2,"info": "Australian walks 100km after outback crash.","done": false},{"id": 3,"info": "Man charged over missing wedding girl.","done": false},{"id": 4,"info": "Los Angeles battles huge wildfires.","done": false}
]
2、再接著打開 store/index.js
,導入 axios 并在 actions 中添加 axios 請求 json 文件獲取數據的代碼,因為數據請求是異步的所以必須在 actions 中實現,如下:
import axios from 'axios'export default new Vuex.Store({state: {// 任務列表list: []},mutations: {initList(state, list) {state.list = list}},actions: {getList(context) {axios.get('/list.json').then(({ data }) => {console.log('data: ', data);context.commit('initList', data)})}}
})
3、打開 toDoList.vue 文件,將 store 中的數據獲取并展示:
<script>
import { mapState } from 'vuex'export default {data() {return {// list:[]}},created(){// console.log(this.$store);this.$store.dispatch('getList')},computed:{...mapState(['list'])}
}
</script>
文本框與store數據的雙向同步
1、在 store/index.js 文件的 state 中新增 inputValue: 'aaa'
字段,并在 mutations 中新增其修改方法 setInputValue
:
// 設置 輸入值setInputValue(state, value) {console.log('value: ', value);state.inputValue = value}
2、在 toDoList.vue 文件的 computed 中映射 inputValue 計算方法:
computed:{...mapState(['list', 'inputValue'])}
3、將 inputValue 計算方法綁定到 a-input
元素的 value 上。
4、為 a-input
元素綁定 change 事件方法 handleInputChange
:
handleInputChange(e) {this.setInputValue(e.target.value)}
添加任務
1、在 store/index.js 文件 state 中新增 nextId: 5
字段,并在 mutations 中編寫 addItem:
// 添加任務項addItem(state) {const item = {id: state.nextId,info: state.inputValue.trim(),done: false}state.list.push(item)state.nextId++state.inputValue = ''}
2、在 toDoList.vue 文件給“添加事項”按鈕綁定點擊事件,編寫處理函數:
addItemToList() {console.log('iv: ', this.inputValue.trim());if (this.inputValue.trim().length <= 0) {return this.$message.warning('文本框內容不能為空!')}this.$store.commit('addItem')}
其他功能實現
store/index.js 文件代碼具體實現:
export default new Vuex.Store({state: {// 任務列表list: [],// 文本框內容inputValue: 'aaa',nextId: 5,viewKey: 'all'},getters: {infoList(state) {if (state.viewKey === 'all') {return state.list} else if (state.viewKey === 'undone') {return state.list.filter(item => !item.done)} else {return state.list.filter(item => item.done)}},unDoneLength(state) {return state.list.filter(item => !item.done).length}},mutations: {// 修改列表數據initList(state, list) {state.list = list},// 設置 輸入值setInputValue(state, value) {console.log('value: ', value);state.inputValue = value},// 添加任務項addItem(state) {const item = {id: state.nextId,info: state.inputValue.trim(),done: false}state.list.push(item)state.nextId++state.inputValue = ''},// 刪除任務項removeItem(state, id) {const index = state.list.findIndex(item => item.id === id)if (index !== -1) {state.list.splice(index, 1)}},// 更改任務完成狀態statusChange(state, params) {const index = state.list.findIndex(item => item.id === params.id)if (index !== -1) {state.list[index].done = params.done}},// 清除完成任務項clearDone(state) {state.list = state.list.filter(item => !item.done)},// 改版列表數據類型changeViewKey(state, key) {state.viewKey = key}},actions: {getList(context) {axios.get('/list.json').then(({ data }) => {console.log('data: ', data);context.commit('initList', data)})}}
})
toDoList.vue 文件具體實現:
<template><div><a-input placeholder="請輸入任務" class="my_ipt" :value="inputValue" @change="handleInputChange"/><a-button type="primary" @click="addItemToList">添加事項</a-button><a-list bordered :dataSource="infoList" class="dt_list" ><a-list-item slot="renderItem" slot-scope="item"><!-- 復選框 --><a-checkbox :checked="item.done" @change="cbStatusChange(item.id, $event)">{{item.info}}</a-checkbox><!-- 刪除鏈接 --><a slot="actions" @click="remoItemById(item.id)">刪除</a></a-list-item><!-- footer區域 --><div slot="footer" class="footer"><!-- 未完成的任務個數 --><span>{{unDoneLength}}條剩余</span><!-- 操作按鈕 --><a-button-group><a-button :type="viewKey === 'all' ? 'primary' : 'default'" @click="changeList('all')">全部</a-button><a-button :type="viewKey === 'undone' ? 'primary' : 'default'" @click="changeList('undone')">未完成</a-button><a-button :type="viewKey === 'done' ? 'primary' : 'default'" @click="changeList('done')">已完成</a-button></a-button-group><!-- 把已經完成的任務清空 --><a @click="clear">清除已完成</a></div></a-list></div>
</template><script>
import { mapState, mapMutations, mapGetters } from 'vuex'
export default {data() {return {}},created() {this.$store.dispatch('getList')},computed: {...mapState(['inputValue', 'viewKey']),...mapGetters(['unDoneLength', 'infoList'])},methods: {...mapMutations(['setInputValue']),handleInputChange(e) {this.setInputValue(e.target.value)},addItemToList() {console.log('iv: ', this.inputValue.trim());if (this.inputValue.trim().length <= 0) {return this.$message.warning('文本框內容不能為空!')}this.$store.commit('addItem')},// 根據 id 刪除對應的任務remoItemById(id) {this.$store.commit('removeItem', id)},cbStatusChange(id, e) {const params = {id,done: e.target.checked}console.log('chang params: ', params);this.$store.commit('statusChange', params)},// 清除已完成clear() {this.$store.commit('clearDone')},changeList(key) {console.log('changeList: ', key);this.$store.commit('changeViewKey', key)}}
}
</script>
1、刪除任務,通過 mutations 修改列表數據實現,詳細實現細節見上述代碼。
2、綁定復選框的選中狀態,同上。
3、修改任務的完成狀態,同上。
4、清除已完成的任務,同上。
5、任務列表數據的動態切換,同上。
6、統計未完成的任務的條數,通過 Getter 加工列表數實現,詳細實現細節見上述代碼