使用Vue开发的朋友应该都知道,Vuex是Vue的状态管理工具,主要用于全局和跨级组件的数据共享管理,它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。同时,Vuex也为开发者提供了多种数据获取与修改的方式,使用方式也很简单,如下代码所示:
第一步、创建一个store
vuex/store.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) // 应用初始状态 const state = { count: 10 } // 定义所需的 mutations const mutations = { incrementM(state,value) { let v = value || 1; state.count += v }, decrementM(state) { state.count-- }, setCount(state, value) { state.count = value } } // actions用于提交mutations,注意action的两种传递参数的方式 const actions = { increment(context){ context.commit('incrementM') //commit里面传递的是mutations 里面定义的方法 }, decrement({commit}){ // 这里其实是es6的语法commit = context.commit commit('decrementM') } } // 规范化的getters等同于针对state的某一属性定制了一个方法 const getters = { getCount(state){ // 针对访问count属性添加一个方法 return state.count } } // 创建 store 实例 export default new Vuex.Store({ actions, getters, state, mutations }) |
第二步、在入口文件(一般是根目录的main.js)引入store,并注入根组件
main.js
1 2 3 4 5 6 7 8 9 10 |
... import store from './vuex/store' import Vuex from 'vuex' new Vue({ router, store, // 根组件注入store render: h => h(App) }).$mount('#app') |
第三步、在组建中使用
在组件中使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
<template> <div> <h1>vuex 测试,再次打开该页面,操作后的状态会保留</h1> <div>直接通过store访问:{{ $store.state.count }}</div> <div>间接通过store访问:{{ count2 }}</div> <div>通过mapState访问:{{ count }}</div> <div>通过mapGetters访问:{{ getCount }}</div> <div> <p>通过mapMutations改变count</p> <button @click="incrementM">+</button> <button @click="decrementM">-</button> <p>通过mapActions改变count</p> <button @click="increment">+</button> <button @click="decrement">-</button> </div> </div> </template> <script> // 引入vuex里面的方法 import { mapState, mapGetters, mapActions, mapMutations} from 'vuex' export default { data(){ return { } }, computed: { count2(){ return this.$store.state.count }, ...mapState({ // ...是es6的语法,用于解构对象,以下同理 count:state=>state.count }), ...mapGetters([ 'getCount', ]) }, methods: { ...mapMutations([ 'incrementM', 'decrementM' ]), ...mapActions([ 'increment', 'decrement' ]) } } </script> |
但是,俗话说得好,物极必反,规则多了便不再是规则。正是由于Vuex为开发者提供了多样化的数据修改于访问方式,导致大家的开发变得很随意,虽然官网给出了推荐的使用方式,但有些人未必会遵守。以 mutation 和 action 为例,虽然推荐使用 action 提交 mutation,但在实际的开发中,还是有很多人直接使用 mutation。我相信有的人虽然知道他们有区别,但是却在自己的项目中看不出区别在哪,还不如直接使用 mutation 来得省事。其实官网也有讲到他们的区别 ,
Action 类似于 mutation,不同在于:
- Action 提交的是 mutation,而不是直接变更状态。
- Action 可以包含任意异步操作。
更详细的讲解请参考https://vuex.vuejs.org/zh/guide/actions.html,其他的我就不多说了。
其他补充
1、如果只是想在组件创建时使用vuex里面的数据进行初始化,后续组件的改动不需要改变vuex的数据,vuex的数据也不影响组件,则可以借助vue的created生命钩子实现(mounted也行,看需求而定):
1 2 3 4 |
created(){ // mounted也行,看需求而定 //只在页面初始化时需要用到vuex状态的使用方式 this.initCount = this.$store.state.count; }, |
2、如果vuex里面的数据要和input等输入框的v-model进行双向绑定,则需使用计算属性的方式,并同时设置 get 和 set,action 里面也要定义 get 和 set。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
// template <input type="number" v-model="modelCount1"> <input type="number" v-model="modelCount2"> // script computed: { // 第一种,通过this.$store直接使用 modelCount1:{ get:function () { return this.$store.state.count }, set (val) { this.$store.commit('setCount', val) } }, modelCount2:{ // 第二种,定义对应的action和getters get:function () { return this.getCount() }, set (val) { this.setCount(val) } } }, methods: { ...mapGetters([ 'getCount', ]), ...mapActions([ 'setCount' ]) }, |
3、一个需要注意的点,考虑如下代码:
vuex/store.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) // 应用初始状态 const state = { count: 10 } const mutations = { increment(state,value) { console.log('异步action 1处:',value) let v = value || 1; state.count += v }, incrementAsync(state,value) { setTimeout(() => { console.log('异步mutation 2处:',value); let v = value || 1; state.count += v },5000) } } const actions = { // 异步action,mutation是同步的 incrementAsync1({commit},value){ setTimeout(()=>{ commit('increment',value) },5000) }, // action是同步的,提交的mutation是异步的 incrementAsync2({commit},value){ commit('incrementAsync',value) } } const getters = { getCount(state){ return state.count } } export default new Vuex.Store({ actions, getters, state, mutations }) |
count.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
<template> <div> <div>通过mapState访问:{{ count }}</div> <div> 异步action <button @click="incrementAsync1(5)">+</button> </div> <div> action提交异步mutation <button @click="incrementAsync2(5)">+</button> </div> <div> 直接异步mutation <button @click="incrementAsync(5)">+</button> </div> </div> </template> <script> // 引入vuex里面的方法 import { mapState,mapMutations,mapActions} from 'vuex' export default { data(){ return { } }, computed: { ...mapState({ // ...是es6的语法,用于解构对象,以下同理 count:state=>state.count }) }, methods: { ...mapMutations([ 'incrementAsync' ]), ...mapActions([ 'incrementAsync1', 'incrementAsync2' ]) } } </script> |
依次点击button,得到的console信息如下
代码和功能都似乎没任何问题。接下来,我们更改一下组件的代码,对 action 和 mutation 不传递参数,store.js不变
1 2 3 4 5 6 7 8 9 10 11 12 |
<div> 异步action <button @click="incrementAsync1">+</button> </div> <div> action提交异步mutation <button @click="incrementAsync2">+</button> </div> <div> 直接异步mutation <button @click="incrementAsync">+</button> </div> |
再依次点击button,得到的console信息如下:
异步action 1处: MouseEvent {isTrusted: true, screenX: 335, screenY: 233, clientX: 335, clientY: 130, …}
异步mutation 2处: MouseEvent {isTrusted: true, screenX: 418, screenY: 254, clientX: 418, clientY: 151, …}
异步mutation 2处: MouseEvent {isTrusted: true, screenX: 387, screenY: 277, clientX: 387, clientY: 174, …}
本来,从代码看,我们想的是在没有传递参数的时候默认加1,结果却收到了一个鼠标事件,导致value为true,所以直接在后面加了个对象的string字符串。这也变相的告诉了我们,Vue的方法在不传递参数的情况下,默认的参数就是events对象(可用如下代码验证),希望大家开发中注意。
1 2 3 4 5 6 7 |
//template <button @click="myfn">测试默认参数</button> //methods myfn(value){ console.log(value) }, |
那么,我们如何判断接收到的参数是不是默认参数呢,通长来说(没有对事件对象进行包装的框架),一个鼠标(包含touch)事件对象都有altKey,ctrlKey,shiftKey三个属性,我们只需判断当前参数是否同时具有这三个属性即可。但是对于input事件,却没有这三个属性,所以光判断altKey,ctrlKey,shiftKey判断不了input事件。
既然上面的方法不适用,我们可以换一种思路,用事件类型判断,鼠标事件为MouseEvent,输入框事件为InputEvent,触摸事件为TouchEvent。
1 2 3 4 5 6 |
myfn(value){ var isEvent = ['MouseEvent','TouchEvent','InputEvent'].filter((item)=>{ return value.constructor.toString().indexOf(item)>0 }).length?true:false; console.log(isEvent) }, |
发表评论