vuex学习总结
state
- 作用:相当于vue中的data,用于存储状态,是唯一数据源,而且是只读,不能做直接修改。从头开始学习Vuex
- 语法:
const store = new Vuex.Store({
state: {
text:'hello world'
}
})
- 调用:
// 方法1
store.state.text
// 方法2(需要使用vue.use注入)
this.$store.state.text
// 方法3,通过辅助函数mapState
import { mapState } from 'vuex';
export default {
computed: {
// 使用ES6的扩展运算符来达到将state释放出来
...mapState([
// 映射 this.text 为 store.state.text
'text'
])
}
}
注意:如果一般state初始化时最好就设置好所需属性,如果真的需要动态需改某一个属性,可以如下:
1)Vue.set(obj, 'newProp', 123)
2)state.obj = { ...state.obj, newProp: 123 }
getter
- 作用:相当于vue中的computed,作用也是类似的。
- 语法:
const store = new Vuex.Store({
state: {
text:'hello world'
},
getters: {
// 第一个参数固定是state对象,第二个参数固定是getters对象
getText: (state, getters) => {
return state.text + ' xiaoming';
},
// getters方法中调用getters方法
getGettersText: (state, getters) => {
return getters.getText;
},
// getters方法返回一个函数
getFuncText: (state,getters) => (name) => {
return state.text + ' ' + name;
}
}
})
- 调用:
// 方法1
store.getters.getText
// 方法2
this.$store.getters.getText
// 方法3,通过辅助函数mapGetters
import { mapGetters } from 'vuex'
export default {
computed: {
...mapGetters([
'getText',
'getGettersText',
'getFuncText'
])
}
}
mutation
- 作用:相当于vue中data对象的的set方法,是唯一可以改变state数据的方式,定位是同步改变,即在mutation的方法中不支持异步逻辑,这个的具体原因是因为mutation类似于事件监听的回调函数,而任何在回调函数中进行的状态的改变都是不可追踪的。
- 语法:
const store = new Vuex.Store({
state: {
text:'hello world'
},
mutations: {
// 第一个参数固定是state,剩下还可以再传入一个或0个参数(也叫载荷),这个载荷参数一般是对象,方便传入多个值
changeText (state, payload) {
// 变更状态
state.text = 'welcome' + payload.name;
}
}
})
- 调用:
// 方法1,只是第二个参数是载荷
store.commit('changeText', {
name: 'xiaoye'
})
// 方法2,相当于整个对象都是载荷
store.commit({
type: 'changeText',
name: 'xiaoye'
})
// 方法3
this.$store.commit('changeText', {
name: 'xiaoye'
})
// 方法4,依赖辅助函数mapMutations
import { mapMutations } from 'vuex';
export default {
methods: {
// 将this.changeText({name: 'xiaoye'})映射为this.$store.commit('changeText', {name: 'xiaoye'});
...mapMutations([
'changeText'
})
}
}
action
- 作用:相当于vue中的method,不能直接修改state,只能通过调用mutation中的方法间接改变state,定位是异步改变,当然也可以同步,如果是需要异步改变的逻辑建议写在action中,其实用过action就知道,其实就相当于把本来属于vue中的method中的方法放到vuex中的action中来而已,只是说公共的异步请求不再用写那么多份在不同的method中,只需要写一份放在action中即可。
- 语法:
const store = new Vuex.Store({
state: {
text:'hello world'
},
mutations: {
// 第一个参数固定是state,剩下还可以再传入一个或0个参数(也叫载荷),这个载荷参数一般是对象,方便传入多个值
changeText (state, payload) {
// 变更状态
state.text = 'welcome' + payload.name;
}
},
actions: {
// 第一个参数是固定的一个类似store的实例,具有和store实例相同方法和属性,即可以直接调用state,getters,commit,dispatch等;第二个参数同mutations一样,是载荷
asynChangeText (context, payload) {
setTimeout(() => {
context.commit('changeText', payload.asynName)
}, 1000)
}
}
})
- 调用:
// 方法1
store.dispatch('asynChangeText', {
asynName: 'xiaoMing'
})
// 方法2
store.dispatch({
type: 'asynChangeText',
asynName: 'xiaoMing'
})
// 方法3
this.$store.commit('asynChangeText', {
asynName: 'xiaoMing'
})
// 方法4,依赖辅助函数mapActions
import { mapActions } from 'vuex';
export default {
// 用法同 mutation 一样
methods: {
...mapActions([
'asynChangeText'
})
}
}
module
- 作用:将state模块化和嵌套子模块化,避免state过大,而变得臃肿,不方便管理,每一个模块都包含完整的state、actions、mutations、getters。
- 语法1(默认命名空间):
/**
** 模块化(默认命名空间)
*/
const moduleA = {
state: {
a: 1,
},
mutations: {
// 该state包含的是本模块自己以及子模块的state
setA (state, payload) {}
},
actions: {
// 该context除包含本模块自己以及子模块的属性和方法外,还另外多包含rootGetters和rootState两个属性根模块(moduleA)的state和getter
getAsynA (context, payload) {}
},
getters: {
// 这里的rootState和rootGetter等同于根模块(moduleA)的state和getter
getA (state, getters, rootState, rootGetter) {}
},
// 两个子模块
modules: {
moduleB,
moduleC
}
}
// 和moduleA模块一样,只是rootState和rootGetter依然表示moduleA这个跟模块而已
const moduleB = {
state: { },
mutations: { ... },
actions: { ... }
}
// 同moduleB
const moduleC = {
state: { ... },
mutations: { ... },
actions: { ... }
}
const store = new Vuex.Store(moduleA)
注意:在默认的命名空间中:
1)所有的模块和子模块的mutations和actions都是包含在全局中的,也叫在全局命名空间中。也就是说如果不同的模块中有相同的actions或者mutations是会存在重复注册挂载到全局中的情况的,只不过他们不是覆盖而是追加的关系而已,被调用的顺讯也是按照模块的注册顺序递归调用的。
2)所有模块和子模块的getters都是包含在全局中的,不过不同的是如果出现不同模块之间重名的情况,不是追加而是先到先得,即哪一个模块先注册,即使用谁的。
3)而state是仍然是划分模块的,外部如果要调用,调用方式也是按照模块的层级路径来调用的。即:moduleA的状态:store.state;moduleB的状态:store.state.moduleB。
- 调用:
// 调用state,方法1
store.state.a
store.state.moduleB.b
// 调用state,方法2
this.$store.state.a
this.$store.state.moduleB.b
// 调用state,方法3
import {mapState} from 'vuex';
export default {
computed: {
...mapState([
'a', // 是moduleA的state
'moduleB', // moduleB是一个对象,包含自己以及子模块的state
'c': state => state.a.moduleC.c // moduleC模块下的state可以通过这种方法的形式返回值
])
}
}
// 调用getter,方法1
store.getters.getA
store.getters.getB
// 调用getter,方法2
this.$store.getters.getA
this.$store.getters.getB
// 调用getter,方法3
import { mapGetters } from 'vuex'
export default {
computed: {
...mapGetters([
'getA',
'getB'
])
}
}
// 调用mutations,方法1,和getter相同点都是挂载在全局中,不同点是不同模块中存在相同名称是会按顺序触发的
store.commit('setA')
store.commit('setB')
// 调用mutations,方法2
store.commit({
type: 'setA'
})
// 调用mutations,方法3
this.$store.commit('setA')
// 调用mutations,方法4
import { mapMutations } from 'vuex';
export default {
methods: {
...mapMutations([
'setA'
})
}
}
// 调用actions,同mutations一样
- 语法2(命名空间):
/**
** 模块化(命名空间)
*/
const moduleA = {
namespaced: true,
state: {
a: 1,
},
mutations: {
// 该state包含的是本模块自己以及子模块的state
setA (state, payload) {}
},
actions: {
// 该context除包含本模块自己以及子模块的属性和方法外,还另外多包含rootGetters和rootState两个属性根模块(moduleA)的state和getter
getAsynA (context, payload) {}
},
getters: {
// 这里的rootState和rootGetter等同于根模块(moduleA)的state和getter
getA (state, getters, rootState, rootGetter) {}
},
// 两个子模块
modules: {
moduleB,
moduleC
}
}
// 和moduleA模块一样,只是rootState和rootGetter依然表示moduleA这个跟模块而已
const moduleB = {
// namespaced: true, // 这里如果不设置命名空间的话那moduleC就继承父命名空间
state: { },
mutations: { ... },
actions: { ... }
}
// 同moduleB
const moduleC = {
namespaced: true,
state: { ... },
mutations: { ... },
actions: { ... },
modules: {
moduleD: {
namespaced: true,
state: {
d: 1,
},
mutations: {},
actions: {}
}
}
}
const store = new Vuex.Store(moduleA)
注意:在命名空间中:
1)所有的模块和子模块的mutations、actions、getters都是包含在各自的命名空间或父命名空间中,即自动根据模块注册的路径调整命名。
2)而state不受影响,因为默认state也已经是层级嵌套的了。
- 调用
// 调用state,方法4(前三个方法和默认命名空间类似)
computed: {
...mapState('moduleC/moduleD', {
d: state => state.d
}),
...mapState('moduleC/moduleD', ['d']) // 这两种是一样的,只是写法不同
},
// 调用state,方法5(通过辅助函数createNamespacedHelpers)
import { createNamespacedHelpers } from 'vuex';
const { mapState } = createNamespacedHelpers('moduleC/moduleD');
export default {
computed: {
// 在 `moduleC/moduleD` 中查找
...mapState({
d: state => state.d
})
}
}
// 其他的getter、mutations、actions也是和state类似的以上的方法
plugins
- 作用:plugins 是vuex中的一个函数,一般配合 subscribe 函数实现类似于拦截器的效果,而且是一个成功之后的拦截器,是每次 mutation 调用成功之后的钩子,它接收store 实例作为作为唯一的参数。
- 语法:
const myPlugin = store => {
// mutation 的格式为:{ type, payload },通过这个对象可以判断是哪一个 mutation 被调用了,额外的参数是什么。
store.subscribe((mutation, state) => {
if (mutation.type === 'updateA') {
console.log(mutation)
console.log(state)
}
})
}
const moduleA = {
state: {
a: 1,
},
mutations: {
// 被调用完成之后会触发 myPlugin 中的 subscribe 回调
updateA (context, payload) {
console.log('A')
}
},
actions: {...},
getters: {...},
modules: {...},
plugins: [myPlugin]
}
export default new Vuex.Store(moduleA);
注意:在插件中如果要修改state状态,也需要通过store调用commit,从而触发 mutation 改变state的状态,也就是说不能直接修改state状态。
strict
- 上面一直强调就是修改只能在 mutations 中完成,为了强制约束,可是在vuex实例化的时候传入该属性(严格模式)。
- 语法
const moduleA = {
state: {...},
mutations: {...},
actions: {...},
getters: {...},
modules: {...},
plugins: [...],
strict: true
}
export default new Vuex.Store(moduleA);