module:可以让每一个模块拥有自己的state、mutation、action、getters,使得结构非常清晰,方便管理。
- 一般结构
const moduleA = {
state: { ... },
mutations: { ... },
actions: { ... },
getters: { ... }
}
const moduleB = {
state: { ... },
mutations: { ... },
actions: { ... }
}
const store = new Vuex.Store({
modules: {
a: moduleA,
b: moduleB
}
})
- 模块内部的数据:
- 内部
state
,模块内部的state
是局部的,也就是模块私有的,比如是car.js
模块state
中的list
数据,我们要通过this.$store.state.car.list
获取; - 内部
getter
、mutation
和action
,仍然注册在全局命名空间内,这是为了多模块可以同时响应同一mutation
;this.$store.state.car.carGetter
的结结果是undefined
,而通过this.$store.state.carGetter
则可以拿到。
传参: getters
====({state(局部状态),getters(全局getters对象),roosState(根状态)}
);
actions
====({state(局部状态),commit,roosState(根状态)}
)
新建一个项目体验一下,通过vue –cli新建一个项目vuemodule, 不要忘记安装vuex.
- 在src 目录下新一个login文件夹,在里面新建
index.js
用于存放login
模块的状态。 为了简单起见,我把模块下的state
,actions
,mutations
,getters
全放在index.js
文件中。
先简单给它增加一个状态,userName: “sam”
const state = {
useName: "sam"
};
const mutations = {
};
const actions = {
};
const getters = {
};
// 不要忘记把state, mutations等暴露出去。
export default {
state,
mutations,
actions,
getters
}
- 在src 目录下,再新建一个
store.js
这是根store
, 它通过modules
属性引入login
模块。
import Vue from "vue";
import Vuex from "Vuex";
Vue.use(Vuex);
// 引入login 模块
import login from "./login"
export default new Vuex.Store({
// 通过modules属性引入login 模块。
modules: {
login: login
}
})
- 在
main.js
中引入store
, 并注入到vue
根实例中。
import Vue from 'vue'
import App from './App.vue'
// 引入store
import store from "./store"
new Vue({
el: '#app',
store, // 注入到根实例中。
render: h => h(App)
})
- 在
app.vue
中通过computed
属性获取到login
下的state
. 这里要注意,在没有modules
的情况下,组件中通过this.$store.state
.属性名 可以获取到,但是有modules
之后,state
被限制到login
的命名空间(模块)下,所以属性名前面必须加模块名(命名空间),组件中通过this.$store.state
.模块名.属性名,在这里是this.$store.state.login.userName
<template>
<div id="app">
<img src="./assets/logo.png">
<h1>{{useName}}</h1>
</div>
</template>
<script>
export default {
// computed属性,从store 中获取状态state,不要忘记login命名空间。
computed: {
useName: function() {
return this.$store.state.login.useName
}
}
}
</script>
组件中成功获取到状态。项目目录和展示如下
- 通过
actions
,mutations
改变名字, 这就涉及到dispatch action
,commit mutations
,mutations
改变state
.
先在login
文件夹 index.js
中添加changeName action
和 CHANGE_NAME mutations
.
const mutations = {
CHANGE_NAME (state, anotherName) {
state.useName = anotherName;
}
};
const actions = {
changeName ({commit},anotherName) {
commit("CHANGE_NAME", anotherName)
}
};
在app.vue
中添加一个按钮:<button> change to json</button>
, 点击时,dispatch
一个 action
. 那在组件中怎么dispatch actions
呢?
在模块中,state
是被限制到模块的命名空间下,需要命名空间才能访问。 但actions
和mutations
, 其实还有 getters
却没有被限制,在默认情况下,它们是注册到全局命名空间下的,所谓的注册到全局命名空间下,其实就是我们访问它们的方式和原来没有module
的时候是一样的。比如没有module
的时候, this.$store.dispatch(“actions”)
, 现在有了modules
, actions
也写在了module
下面(changeName
写到了login
目录下的index.js
中),我们仍然可以这么写,this.$store.dispatch(“changeName”)
, 组件中的getters
, 也是通过 this.$store.getters.module中getters
来获取。
<template>
<div id="app">
<img src="./assets/logo.png">
<h1>{{useName}}</h1>
<!-- 添加按钮 -->
<div>
<button @click="changeName"> change to json</button>
</div>
</div>
</template>
<script>
export default {
// computed属性,从store 中获取状态state,不要忘记login命名空间。
computed: {
useName: function() {
return this.$store.state.login.useName
}
},
methods: {
// 和没有modules的时候一样,同样的方式dispatch action
changeName() {
this.$store.dispatch("changeName", "Jason")
}
}
}
- 局部参数
虽然dispatch action
和 commit mutations
可以全局使用,但是写在module
中的actions
, mutations
和getters
, 它们获得的默认参数却不是全局的,都是局部的,被限定在它们所在的模块中的。比如mutations
和getters
会获得state
作为第一个默认参数,这个state
参数,就是限定在mutations
和getters
所在模块的state
对象,login
文件夹下的mutations
和getters
只会获取到当前index.js
中的 state
作为参数 。actions
会获得一个context
对象作为参数,这个context
对象就是当前module
的实例,module
相当于一个小store
.
那么怎样才能获取到根store
中的state
和 getters
呢? Vuex
提供了 rootState
, rootGetters
作为module
中 getters
中默认参数, actions
中context
对象,也会多了两个属性,context.getters
, context. rootState
, 这些全局的默认参数,都排在局部参数的后面。
我们在store.js
中添加state
, getters
:
export default new Vuex.Store({
// 通过modules属性引入login 模块。
modules: {
login: login
},
// 新增state, getters
state: {
job: "web"
},
getters: {
jobTitle (state){
return state.job + "developer"
}
}
})
login
目录下的 index.js
app.vue
增加h2
展示 loacaJobTitle
, 这个同时证明了getters
也是被注册到全局中的。
<template>
<div id="app">
<img src="./assets/logo.png">
<h1>{{useName}}</h1>
<!-- 增加h2 展示 localJobTitle -->
<h2>{{localJobTitle}}</h2>
<!-- 添加按钮 -->
<div>
<button @click="changeName"> change to json</button>
</div>
</div>
</template>
<script>
import {mapActions, mapState,mapGetters} from "vuex";
export default {
// computed属性,从store 中获取状态state,不要忘记login命名空间。
computed: {
...mapState({
useName: state => state.login.useName
}),
// mapGeter 直接获得全局注册的getters
...mapGetters(["localJobTitle"])
},
methods: {
changeName() {
this.$store.dispatch("changeName", "Jason")
}
}
}
</script>
- 其实
actions
,mutations
,getters
, 也可以限定在当前模块的命名空间中。只要给我们的模块加一个namespaced
属性:
const state = {
useName: "sam"
};
const mutations = {
CHANGE_NAME (state, anotherName) {
state.useName = anotherName;
}
};
const actions = {
changeName ({commit, rootState},anotherName) {
if(rootState.job =="web") {
commit("CHANGE_NAME", anotherName)
}
},
alertName({state}) {
alert(state.useName)
}
};
const getters = {
localJobTitle (state,getters,rootState,rootGetters) {
return rootGetters.jobTitle + " aka " + rootState.job
}
};
// namespaced 属性,限定命名空间
export default {
namespaced:true,
state,
mutations,
actions,
getters
}
当所有的actions
, mutations
, getters
都被限定到模块的命名空间下,我们dispatch actions
, commit mutations
都需要用到命名空间。如 dispacth("changeName")
, 就要变成 dispatch("login/changeName")
; getters.localJobTitle
就要变成 getters["login/localJobTitle"]
app.vue 如下:
<template>
<div id="app">
<img src="./assets/logo.png">
<h1 @click ="alertName">{{useName}}</h1>
<!-- 增加h2 展示 localJobTitle -->
<h2>{{localJobTitle}}</h2>
<!-- 添加按钮 -->
<div>
<button @click="changeName"> change to json</button>
</div>
</div>
</template>
<script>
import {mapActions, mapState,mapGetters} from "vuex";
export default {
// computed属性,从store 中获取状态state,不要忘记login命名空间。
computed: {
...mapState("login",{
useName: state => state.useName
}),
localJobTitle() {
return this.$store.getters["login/localJobTitle"]
}
},
methods: {
changeName() {
this.$store.dispatch("login/changeName", "Jason")
},
alertName() {
this.$store.dispatch("login/alertName")
}
}
}
</script>
有了命名空间之后,mapState
, mapGetter
s, `mapActions 函数也都有了一个参数,用于限定命名空间,每二个参数对象或数组中的属性,都映射到了当前命名空间中。
<script>
import {mapActions, mapState,mapGetters} from "vuex";
export default {
computed: {
// 对象中的state 和数组中的localJobTitle 都是和login中的参数一一对应。
...mapState("login",{
useName: state => state.useName
}),
...mapGetters("login", ["localJobTitle"])
},
methods: {
changeName() {
this.$store.dispatch("login/changeName", "Jason")
},
...mapActions('login', ['alertName'])
}
}
</script>
好了,合适自己的才是好的.