vuex其实就是一个大的全局管理容器/仓库,你可以在你项目的任何地方用到它,并且store的状态是响应式的,也就是说在某一个组件里修改store,则可以得到全局的响应变更。
不能直接更改store中的状态,改变store中的状态唯一途径就是显示的提交(commit)
例如:
// 如果在模块化构建系统中,请确保在开头调用了 Vue.use(Vuex)
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
}})
// 不要直接改变store.state.count
store.commit('increment') // 提交
console.log(store.state.count) // 1
由于store中的状态是响应式的,在组件中调用store只需在计算属性中返回即可,触发变化也仅仅是在组件methods中提交mutation。
在组件中获取store中的状态
① 通过组件中计算属性返回
Vuex 通过 store 选项,提供了一种机制将状态从根组件“注入”到每一个子组件中(需调用 Vue.use(Vuex)):
const app = new Vue({
el: '#app',
// 把 store 对象提供给 “store” 选项,这可以把 store 的实例注入所有的子组件
store,
components: { Counter },
template: `
<div class="app">
<counter></counter>
</div>
`})
const Counter = {
template: `<div>{{ count }}</div>`,
computed: {
count () {
return this.$store.state.count
}
}}
② 使用getter
getter其实就相当于是store的计算属性,来实时监听state值的变化(最新状态)
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
const store = new Vuex.Store({
state: {
todos: [
{ id: 1, text: '...', done: true },
{ id: 2, text: '...', done: false }
]
},
getters: {
doneTodos: state => {
return state.todos.filter(todo => todo.done)
}
}})
通过属性访问getter
store.getters.doneTodos // -> [{ id: 1, text: '...', done: true }]
getter也可以接受其他getter作为第二个参数
getters: {
// ...
doneTodosCount: (state, getters) => {
return getters.doneTodos.length
}}
store.getters.doneTodosCount // -> 1
通过方法访问
让getter返回一个函数,以函数的参数给getter传参
getters: {
getTodoById: (state) => (id) => {
return state.todos.find(todo => todo.id === id)
// 也可以采用下面这种写法
getTodoById(state) {
return (id) => {
return state.todos.find(todo => todo.id === id)
}
}}
store.getters.getTodoById(2) // -> { id: 2, text: '...', done: false }
mutation
- mutation 必须是同步操作
更改 Vuex 的 store 中的状态的唯一方法是提交 mutation
每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。
const store = new Vuex.Store({
state: {
count: 1
},
mutations: {
increment (state) {
// 变更状态
state.count++
}
}})
上例中的increment是事件类型type,后面是回调函数handler,要调用一个mutation的handler,必须以相应的type调用store.commit
store.commit('increment');
当然,也可以向store.commit传入额外的参数
mutations: {
increment (state, payload) {
state.count += payload.count
}
}
// 可以这样传参
store.commit('increment', {
count: 10
})
// 也可以这样传参
store.commit({
type: 'increment',
count: 10
})
其实更建议使用常量命名 Mutation 事件类型
mutations: {
SOME_MUTATION: (state, payload) => {
state.count += payload.count
}
}
action
- action提交的是mutation, 不是直接改变状态
- 异步操作
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment (context) {
context.commit('increment')
}
// 也可以这样
increment ({ commit }) {
commit('increment')
}
}
})
context是一个与 store 实例具有相同方法和属性的 对象, 因此可以context.commit 或者 context.state、context.getter来获取
在action中执行异步操作
actions: {
incrementAsync ({ commit }) {
setTimeout(() => {
commit('increment')
}, 1000)
}
}
action是通过store.dispatch触发
store.dispatch('incrementAsync')
// 也可以传参
// 以载荷形式分发
store.dispatch('incrementAsync', {
amount: 10
})
// 以对象形式分发
store.dispatch({
type: 'incrementAsync',
amount: 10
)
mapState、mapGetter、mapMutation、mapAction辅助函数
这些辅助函数,其实都是帮我们在组件中映射相应的状态,减少不必要的代码
例如:
// store文件中
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment (context) {
context.commit('increment')
}
},
getters: {
getCount(state) {
return state.count
}
}
})
// 组件中
import { mapState,mapGetter,mapMutation,mapAction} from 'vuex'
export default {
computed: {
...mapState({
count: state => state.count
}),
...mapState([
'count', // 将this.count映射为this.$store.state.count
]),
...mapGetters([
'getCount', // 将this.getCount映射为this.$store.getters.getCount
]),
...mapGetters({
getCount: getCount,
})
},
methods: {
...mapAction([
'increment', // 将 `this.increment()` 映射为`this.$store.dispatch('increment')`
]),
...mapActions({
add: 'increment' // 将 `this.add()` 映射为 `this.$store.dispatch('increment')`
}),
...mapMutations({
add: 'increment' // 将 `this.add()` 映射为 `this.$store.commit('increment')`
}),
...mapMutations([
'increment', // 将 `this.increment()` 映射为`this.$store.commit('increment')`
]),
}
}
vuex中的module模块化
当一个项目过于复杂需要共享的状态过多时,store对象就会变得非常臃肿且不好管理,这时我们就可以使用vuex提供的将store分割成一个个module
如何使用?
首先在store文件夹下新建module文件夹,里面就是管理状态的js文件,既然要把不同状态分开,那就建立不同的文件
此时store文件夹下的index文件就要改成如下所示:
import Vue from 'vue';
import Vuex from 'vuex';
import a1 from './modules/a1';
import a2 from './modules/a2';
Vue.use(Vuex);
export default new Vuex.Store({
modules:{
a1,
a2
}
});
默认情况下,模块内部的action等是注册在全局命名空间的,如果你希望你的文件具有更高的封装性和复用性,可以通过添加namespaced:true使其成为带命名空间的模块。
而我们如何在组件中使用带有命名空间的模块?
举个栗子:
// a1.module.js
const a1 = {
namespaced: true,
state: {
flag: false
},
mutations: {
CHANGE_FLAG: (state) => {
state.flag = true;
}
},
actions: {
changeFlag({commit}) {
commit('CHANGE_FLAG');
}
}
}
export default a1;
// 组件中
<tempalte>
<div>
<div v-if="flag"> 显示 </div>
<div v-else> 隐藏 </div>
</div>
</tempalte>
import {mapState , mapActions} from 'vuex';
export default {
name: 'A1',
data() {
return {}
},
computed: {
// a1 表示指的是modules文件夹下的a1.module.js文件
...mapState('a1', {
flag: state => state.flag
}),
// 也可以这样写
...mapState({
flag: state => state.a1.flag
}),
// 不使用mapState
flag() {
return this.$store.state.a1.flag
}
},
methods: {
...mapActions('a1', ['changeFlag'])
// 此时将this.changeFlag映射为 this.$store.dispatch('a1/changeFlag')
}
}
至此,简单的使用vuex你学会了吗?