在使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割:
注意:
//src根目录下的store.js不能删掉,但是可以注释掉不用
如何使用module
moduleA.js文件内容如下:
//在store文件夹下新建modules文件夹,并在下面建立moduleA.js和moduleB.js文件用来存放vuex的modules模块
const state = {
stateA: 'A'
}
const mutations = {
showA (state) {
return state.stateA
}
}
const actions = {
showAAction (context) {
context.commit('showA')
}
}
const getters = {
getA (state) {
return state.stateA
}
}
export default {namespaced: true,state, mutations, actions, getters}
moduleB.js文件内容如下:
const state = {
stateB: 'B'
}
const mutations = {
showA (state) {
return state.stateB
}
}
const actions = {
showAAction (context) {
context.commit('showB')
}
}
const getters = {
getA (state) {
return state.stateB
}
}
// namespaced 属性,限定命名空间
export default {namespaced: true,state, mutations, actions, getters}
或者
//方法2
export default {
state:{
moduleBstate: 'moduleBstate'
},
mutatons:{},
actions:{},
getters:{}
// namespaced: true,//默认为false
}
//namespaced写成true,actions, mutations, getters, 可以限定在当前模块的命名空间中、意思就是可以用这个module名作为区分了(也就是module所在的文件夹名)
//比如import moduleA from './modules/moduleA/moduleA'//引进模块 moduleA文件夹名作为区分、作为当前模块的命名
store.js 文件内容如下:
//state mutations actions 可以分别独立j创建js文件,然后再引进store.js或者当前模块
import Vue from 'vue'
import Vuex from 'vuex'
import state from './state'
import mutations from './mutations' //引进独立创建的mutations .js
import getters from './getters'//引进独立创建的getters.js
import actions from './actions'//引进独立创建的actions.js
import moduleA from './modules/moduleA/moduleA'//引进模块
import moduleB from './modules/moduleB/moduleB'
Vue.use(Vuex)
const store = new Vuex.Store({
state,//独立js文件
mutations,//独立创建的mutations.js文件
getters,
actions,
modules: {
moduleA,
moduleB
}
export default store
//或者这样
export default new Vuex.Store({
state: {
a : true
},
mutations: {
},
actions: {
},
//vueX模块
modules:{
moduleA,
moduleB
}
})
//一个js文件可以用两个或多个模块
const modules1 = {
state: {
a: '我是模块化1'
},
mutations: {},
actions: {}
}
const modules2 = {
state: {
a: '我是模块化2'
},
mutations: {},
actions: {}
}
export default {
modules1,
modules2
}
在组件中使用
<template>
<div class="modules">
<h1>{{moduleA}} --- {{moduleB}} --- {{getmoduleA}}</h1>
//namespaced 属性,限定命名空间
<h1 @click ="alertName">{{useName}}</h1>
<button @click="changeName"> change to json</button>
</div>
</template>
<script>
import {mapActions, mapState,mapGetters} from "vuex";
export default {
data () {
return {}
},
computed: {
...mapState({
moduleA: state => state.moduleA.stateA,
moduleB: state => state.moduleB.stateB
})
//命名空间写法
...mapState("moduleA",{//moduleA为命名空间,指定modules
moduleA: state => state.moduleA.stateA,
}),
//指定imoduleA的getter
getmoduleA() {
return this.$store.getters["moduleA/getA "]
}
...mapGetters("moduleA", ["getA "])
},
methods: {
changeName() {
this.$store.dispatch("moduleA/showAAction ", "moduleA")
},
...mapActions('moduleA', ['showAAction '])
}
}
</script>
store.js index.js,具体参考vue-DYNAMIC-ROUTING-DEMO
modules: {
moduleA,
moduleB,
moduleC,
},
// 获取指定模块state
console.log(this.$store.state.moduleA.moduleAstate)
console.log(this.$store.state.moduleB.modulesBstate);
// 获取所有的state
console.log(this.$store.state);
// 获取所有的getters(包括模板的),不需要指定模块
console.log(this.$store.getters);
// 使用module中的getter
// module中的getter,又分为namespaced(命名空间)为true和false的情况。
// 默认为false, 则表示方位都是全局注册(this.$store.getters获取所有的getter)
// 无法获取指定模板的getters,例如this.$store.getters["moduleA/moduleAstate2"]获取
// 当为true时,可以获取指定模板getters,则使用如下方法:
console.log(this.$store.getters["moduleA/moduleAstate2"]);
// mutations 和 actions 也是分的模块
// 使用module中的 actions和mutations,又分为namespaced(命名空间)为true和false的情况。
// 默认为false, 则表示方位都是全局注册(this.$store.commit('mutationsAstate'))可以调用所有的mutations方法和action方法
// 当为true时,可以调用指定模板mutations方法和action方法,则使用如下方法:
this.$store.commit('moduleA/mutationsAstate');
console.log("按钮权限-", this.$store.getters.buttonObj)
1、模块动态注册
在 store 创建之后,你可以使用 store.registerModule 方法注册模块:
// 注册模块 `myModule`
store.registerModule('myModule', {
state:{},
getters:{},
mutations:{}
actions{}
})
// 注册嵌套模块 `nested/myModule`
store.registerModule(['nested', 'myModule'], {
})
之后就可以通过 store.state.myModule 和 store.state.nested.myModule 访问模块的状态。模块动态注册功能使得其他 Vue 插件可以通过在 store 中附加新模块的方式来使用 Vuex 管理状态。例如,vuex-router-sync 插件就是通过动态注册模块将 vue-router 和 vuex 结合在一起,实现应用的路由状态管理。你也可以使用 store.unregisterModule(moduleName) 来动态卸载模块。注意,你不能使用此方法卸载静态模块(即创建 store 时声明的模块)。
2.卸载动态模块
this.$store.unregisterModule("myModule")
3.举个例子及实现demo
在 Vue 组件中获得 Vuex 状态:state
获取state的几种方法:
1
export default{
computed:{
orderList(){
return this.$store.state..;
}
}
}
2
mapState 辅助函数映射到当前组件:
import { mapState} from "vuex";
export default {
// ...
computed: mapState({
// 箭头函数可使代码更简练
count: state => state.count,
// 传字符串参数 'count' 等同于 `state => state.count`
countAlias: 'count',
// 为了能够使用 `this` 获取局部状态,必须使用常规函数
countPlusLocalState (state) {
return state.count + this.localCount
}
})
}
//当映射的计算属性的名称与 state 的子节点名称相同时,我们也可以给 mapState 传一个字符串数组,意思就是直接使用this.count计算属性
computed: mapState([
// 映射 this.count 为 store.state.count
'count'
])
Getter
有时候我们需要从 store 中的 state 中派生出一些状态,处理一些数据、例如对列表进行过滤并计数:
computed: {
doneTodosCount () {
return this.$store.state.todos.filter(todo => todo.done).length
}
}
如果有多个组件需要用到此属性,我们要么复制这个函数,或者抽取到一个共享函数然后在多处导入它——无论哪种方式都不是很理想。
Vuex 允许我们在 store 中定义“getter”(可以认为是 store 的计算属性)。就像计算属性一样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。
Getter 接受 state 作为其第一个参数:
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 对象,你可以以属性的形式访问这些值:
store.getters.doneTodos // -> [{ id: 1, text: '...', done: true }]
Getter 也可以接受其他 getter 作为第二个参数:
getters: {
// ...
doneTodosCount: (state, getters) => {
return getters.doneTodos.length
}
}
store.getters.doneTodosCount // -> 1
我们可以很容易地在任何组件中使用它:
computed: {
doneTodosCount () {
return this.$store.getters.doneTodosCount
}
}
通过方法访问
你也可以通过让 getter 返回一个函数,来实现给 getter 传参。在你对 store 里的数组进行查询时非常有用。
etters: {
// ...
getTodoById: (state) => (id) => {
return state.todos.find(todo => todo.id === id)
}
}
store.getters.getTodoById(2) // -> { id: 2, text: '...', done: false }
//注意,getter 在通过方法访问时,每次都会去进行调用,而不会缓存结果。
mapGetters 辅助函数
mapGetters 辅助函数仅仅是将 store 中的 getter 映射到局部计算属性:
import { mapGetters } from 'vuex'
export default {
// ...
computed: {
// 使用对象展开运算符将 getter 混入 computed 对象中
...mapGetters([
'doneTodosCount',
// ...
])
}
}
//如果你想将一个 getter 属性另取一个名字,使用对象形式:
...mapGetters({
// 把 `this.doneCount` 映射为 `this.$store.getters.doneTodosCount`
doneCount: 'doneTodosCount'
})
demo
//在这使用...mapGetters(['shoplist'])映射的数据
<tr v-for="n in shoplist">
<td>{{n.id}}</td>
<td>{{n.name}}</td>
<td>{{n.price}}</td>
<td><span @click="addtocart(n)">加入购物车</span></td>
</tr>
<script>
import {mapState,mapGetters,mapActions } from 'vuex' //导入辅助函数
export default {
computed:{
//mapGetters辅助函数仅仅是将 store 中的 getter ['shoplist']映射到局部计算属性
//获取到getter的shoplist数据
...mapGetters(['shoplist'])//把store文件getter里面shoplist映射到组件里,拿到的是数据,不是函数
}
}
</script>
store文件:
//向外输出数据的方法,然后怎么把这些数据弄到需要的组件上呢?
//在需要数据的组件import {mapState,mapGetters,mapActions } from 'vuex' //导入辅助函数
//然后...mapGetters(['shoplist'])
const getters ={
//商品列表数据
shoplist:state=>{
return state.shoplist
}
Mutation
Mutation可以进行vuex的状态修改、并且它会接受 state 作为第一个参数:
在项目中,经常使用到VUEX状态管理,对于小项目中,直接使用下述述两种方法即可。
this.$store.commit('increment ','a') //increment事件类型,即mutation的方法名 a需要传的参数
或者
this.$store.dispatch('increment ','a')//a需要传的参数
//对象风格的提交方式
//提交 mutation 的另一种方式是直接使用包含 type 属性的对象:
store.commit({
type: 'increment',//事件类型
amount: a //需要传的参数
})
//在store的mutations中注册一个名为increment的监听及监听触发后的函数体,
//当监听到名为'increment'的commit事件时,会触发store中mutations下increment函数
const store = new Vuex.Store({
state: {
count: 1
},
//提交载荷(Payload)
//mutation的载荷payload是作为store.commit传入的额外参数存在的。
mutations: {
increment (state,payload) {//载荷payload接受commit或者dospatch传过的参数
// 变更状态
state.count++
}
}
// 另一种写法
mutations: {
increment: {
handler: () => {
// 逻辑函数
},
// 事件类型
type: 'increment'
}
}
})
但是,当项目中的 mutation 或者 action 过多的时候,这样一个个的写就显得比较麻烦。
你可以在组件中使用 this.$store.commit('xxx') 提交 mutation,或者使用 mapMutations 辅助函数将组件中的 "methods" 映射为 store.commit 调用(需要在根节点注入 store)。
import { mapMutations } from 'vuex'
export default {
// ...
methods: {
...mapMutations([
'increment', // 将 `this.increment()` 映射为 `this.$store.commit('increment')`
// `mapMutations` 也支持载荷
//mutation的载荷payload是作为store.commit传入的额外参数存在的。
'incrementBy' // 将 `this.incrementBy(amount)` 映射为 `this.$store.commit('incrementBy', amount)`
]),
...mapMutations({
add: 'increment' // 将 `this.add()` 映射为 `this.$store.commit('increment')`
})
//add是我们自己定义的事件名称,increment是action的事件类型。当我们自己定义的事件名称与action的事件类型相同,可写成这样:
...mapMutations(["increment"])
}
}
demo
页面文件:
<tr>
<td><span @click="add">加入购物车</span></td>
<td><span @click="reduce">加入购物车</span></td>
</tr>
<script>
import {mapActions } from 'vuex' //导入辅助函数
export default {
methods:{
...mapMutations(['add' , 'reduce'])//通过点击事件add和reduce调用 提交到store.js的mutations
}
}
</script>
样的写法可以让我们在组件中像写普通方法一样,直接调用mutations里的方法,把mutations里的方法名直接写在需要调用的地方就行
但是mutations对象中的方法是可以传参的(payload),那么在mapMutations中如何传参呢?
<tr>
<td><span @click="add(5)">加入购物车</span></td>
<td><span @click="reduce">加入购物车</span></td>
</tr>
<script>
import {mapMutations} from 'vuex' //导入辅助函数
export default {
methods:{
//add的参数会自动映射store文件的mapMutations
...mapMutations(['add' , 'reduce'])//通过点击事件add和reduce调用 提交到store.js的mutations
}
}
在store.js里面
mutations: {
add(state,n) {//载荷n接受commit或者dospatch传过的参数
// 变更状态
},
reduce(state) {//无传参
// 变更状态
},
}
mapActions mapMutations 必须放在 methods中,因为 action 或者 mutation 都是方法.
所以,vue提供了 mapActions和mapMutations 。两者使用方法相似,下面以 mapActions为例。
Actions
Action 类似于 mutation,不同在于:
Action 提交的是 mutation,而不是直接变更状态,也就是在Action处理好异步,再提交到mutation修改。
Action 可以包含任意异步操作。
注册一个简单的 action:
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment (context) {
context.commit('increment')
}
}
})
Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,因此你可以调用 context.commit 提交一个 mutation,或者通过 context.state 和 context.getters 来获取 state 和 getters。
实践中,我们会经常用到 ES2015 的 参数解构 来简化代码(特别是我们需要调用 commit
很多次的时候):
actions: {
increment ({ commit }) {
commit('increment')
}
}
分发 Action
Action 通过 store.dispatch 方法触发:
store.dispatch('increment')
乍一眼看上去感觉多此一举,我们直接分发 mutation 岂不更方便?实际上并非如此,还记得 mutation 必须同步执行这个限制么?Action 就不受约束!我们可以在 action 内部执行异步操作:
actions: {
incrementAsync ({ commit }) {
setTimeout(() => {
commit('increment')
}, 1000)
}
}
//Actions 支持同样的载荷方式和对象方式进行分发:
// 以载荷形式分发
store.dispatch('incrementAsync', {
amount: 10
})
// 以对象形式分发
store.dispatch({
type: 'incrementAsync',
amount: 10
})
demo
<template>
</template>
<script>
export default{
data(){
return {
}
},
methods:{
},
created(){//created一般都是需要异步操作
//通过store的dispatch方法分发任务给action
this.$store.dispatch("setPlayStatus",{
playStatus:true //这里的值还没改的,只是分发到action,在mutations才真正的改
})
}
}
</script>
store.js
import Vue from 'vue'
import Vuex from 'vuex'
import state from '@/vuex/state.js'
Vue.use(Vuex)
export default new Vuex.Store({
state: { //声明全局变量任何组件都可以拿到他
token:false, //修改token的值:例子在home页面提交数据给stores.js的mutations修改
playStatus:false , //异步修改playStatus的值:
},
mutations: {//改变state状态的唯一方法 commit发送过来,通过mutations才是改变的 、、不支持异步
setToken(state,data){
/*
* 这个函数接收两个参数,
* state:指的是vuex中的state属性
* data:用户通过commit发送过来的数据
*/
state.token=data.token //改变state的token的值
console.log(state,data);
},
//异步修改状态
setPlayStatus(state,data){
state.playStatus = data.playStatus;
console.log("state",state) //state是vuex的state属性
}
},
actions:{
//解决异步请求
//再提交给mutations修改 {commit} 是对象解构赋值,因为action里面的方法默认接受一个参数context,
setPlayStatus({commit},data){//context的实例化对象
console.log("context",context)
commit("setPlayStatus",data)
}
},
getters:{//相当于实例中的计算属性,用法跟计算属性一样
}
})
mapActions辅助函数分发actions
import { mapActions } from 'vuex'
export default {
// ...
methods: {
...mapActions([
'increment', // 将 `this.increment()` 映射为 `this.$store.dispatch('increment')`
// `mapActions` 也支持载荷:
'incrementBy' // 将 `this.incrementBy(amount)` 映射为 `this.$store.dispatch('incrementBy', amount)`
]),
...mapActions({
add: 'increment' // 将 `this.add()` 映射为 `this.$store.dispatch('increment')`
})
//add是我们自己定义的事件名称,increment是action的事件类型。当我们自己定义的事件名称与action的事件类型相同,可写成这样:
...mapActions(["increment"])
}
}
mapActions 必须放在 methods中,因为 action 或者 mutation 都是方法.
demo2
<button @click="add(5)">+1</button>
//组件分发
//简写
...mapActions({
add: "increment"
})
//实际上的写法
methods:{
increment(n){
this.$store.dispatch("increment",n)
}
}
add是我们自己定义的事件名称,increment是action的事件类型。当我们自己定义的事件名称与action的事件类型相同
<button @click="increment(5)">+1</button>
...mapActions(["increment"])
store.js
mutations: {//改变state状态的唯一方法 commit发送过来,通过mutations才是改变的 、、不支持异步
INCREMENT(state,data){
/*
* 这个函数接收两个参数,
* state:指的是vuex中的state属性
* data:用户通过commit发送过来的数据
*/
console.log(state,data);
},
actions: {
//分发到mutation修改状态
increment({commit},data){
commit("INCREMENT",data)
},
}
//{commit} 是对象解构赋值,因为action里面的方法默认接受一个参数context,
//context是store的一个实例,这个实例是一个对象,改写法是表明使用context中的commit方法。es6的语法
辅助函数actions传参跟mutations一样
例子1:
页面文件:
<tr v-for="n in shoplist">
<td>{{n.id}}</td>
<td>{{n.name}}</td>
<td>{{n.price}}</td>
<!--把点击的那一项作为参数传递过去-->
<td><span @click="addtocart(n)">加入购物车</span></td>
</tr>
<script>
import {mapState,mapGetters,mapActions } from 'vuex' //导入辅助函数
export default {
methods:{
...mapActions(['addtocart'])//引进store文件actions定义的addtocart函数,通过点击事件addtocart调用
}
//这里的意思是通过模块名来获取到login,renewToken这个函数,(common就是模块名)
...mapActions("common", ["addtocart", 'renewToken']),//通过引进引进store文件actions定义的login函数
}
</script>
store文件
const mutations={
//添加到购物车的操作
addtocart(state.data){
//
}
const actions={
//添加到购物车的操作
addtocart({commit},n){
//console.log(n.name);
//提交到mutations修改原数组
commit('add',{
id:n.id
})
}
例子2:
<script>
ecport default{
methods:{
//这里的意思是通过模块名来获取到addtocart,renewToken这个函数,(config就是模块名)
...mapActions("common", ["addtocart", 'renewToken']),//通过引进引进store文件actions定义的addtocart、renewToken函数
async handleLogin (){
try {
this.errorMessage = "";
const res = await this.login(this.loginForm);//this.loginForm是一个对象, 在这调用引进的login(this.loginForm)
}catch(err){
console.error(err);
}
}
}
}
</script>
Vue项目中使用Vuex + axios发送请求 转自https://www.cnblogs.com/junwu/p/11195596.html
computed: {
geneObj () { // 获取store里的数据,放在computed中可以实时更新
return this.$store.state.geneObj ;
}
},
methods:{
getGene(){
this.$store.dispatch('getGeneAction');//触发actions里的saveForm函数,调动接口
//getDatamingData().then(this.getGeneSucc);
/*
axios.get(urlApi.datamining)
.then(this.getGeneSucc);
*/
},
//也可以通过actions发送请求,在当前组件处理数据:
getGene() {
var _vm = this;//避免this不是这些vue实力
_vm.$store.dispatch('getGeneAction').then(function(res) { // 回调函数是普通函数
console.log(res);
})
}
//或:
getGene() {
this.$store.dispatch('getGeneAction').then(function(res) { // 回调函数是普通函数
console.log(res);
}.bind(this))
}
}
*注释掉的是以前直接在方法中获取接口数据
在我的store.js
state:{
geneObj :" "
},
action:{
getGeneAction(context,item){
console.log('getGeneAction');
//调用axios
getDatamingData().then((res) => {
context.commit('GETGENEMUTATION',res);
});
}
},
//派发一个GETGENEMUTATION mutation
mutation:{
GETGENEMUTATION(state,item){
//console.log(item.data.obj);
state.geneObj = item.data.obj;
}
}
Vuex:不同模板(module)之间分发 action 或提交 mutation
————————————————
版权声明:本文为CSDN博主「不愿意透露姓名的攻城诗」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/github_35830425/article/details/88670982
场景:卖书的商城,在书籍商品显示详情页的时候需要去获取相关的作者信息,书籍表通过authorId字段关联作者表中的作者。所以考虑方案:在详情页先获取书籍信息,然后通过authorId去查找作者信息。
实现:创建两个状态管理的module:book.js 和 author.js ,然后在详情页:bookDetail.vue中去管理状态。
// author.js
export default {
namespaced: true,
state: {
author: {}
},
getters: {},
mutations: {
setAuthor: function (state, author) {
state.author = author
}
},
actions: {
//获取作者详情
getAuthorName: function ({ commit }, authorId) {
Author.getAuthorById(authorId).then(res=> {
commit('setAuthor', res.author)
})
}
}
}
// book.js
export default {
namespaced: true,
state: {
// 正在查看的书本详情
currentBook: {}
},
getters: {
// ...
showBook (state) {
return state.currentBook
}
},
mutations: {
// ...
setCurrentBook (state, book) {
state.currentBook = book
}
},
actions: {
// ...
// 根据id获取书籍详情
getBookDetailById ({ commit, dispatch }, id) {
// Shop.getBookDetailById是api
Shop.getBookDetailById (id).then(res = >{
//分发到mutation获取书籍详情
commit('setCurrentBook', res.book)
// 获取作者详情
// todo: {root:true} 很重要,如果没有添加的话,此处会报错
//分发到 author.js模板的mutations
dispatch('BookDetail/getAuthorName', res.book.authorId, { root: true })
})
}
}
}
刚开始做的时候没有加{ root : true }导致报错,查找文档资料后发现以下描述:
在带命名空间的模块内访问全局内容(Global Assets)
如果你希望使用全局 state 和 getter,rootState 和 rootGetter 会作为第三和第四参数传入 getter,也会通过 context 对象的属性传入 action。
若需要在全局命名空间内分发 action 或提交 mutation,将 { root: true } 作为第三参数传给 dispatch 或 commit 即可。
所以需要分发其他模板的action或者提交其他模板的mutation的时候,需要加上{ root: true }
然后只需在bookDetail.vue页面去获取状态即可
// bookDetail.vue
export default {
name: 'ProductDetail',
state: {
return () {
// ...
'bookId': '201903190001'
}
},
computed: {
// ...
// 获取当前书籍信息
// 可直接通过 this.book 使用
...mapState('book', {
book: 'currentBook'
}),
// 获取作者信息
// 可直接通过 this.author 使用
...mapState('author', {
author: 'author'
})
},
methods: {
// ...
...mapActions({//book/getBookDetailById命名空间、调用指定名字为book的moduls的action
getBookDetailById: 'book/getBookDetailById'
})
},
created () {
// 获取商品详情 就是mapActions定义的事件名getBookDetailById
this.getBookDetailById(this.bookId)
}
}
</script>
...
}