vuex是什么, state,getters,mutations,actions,modules的用途和用法
vuex 是一个状态管理工具,它采用集中式存储管理应用的所有组件的状态,当有多个组件共享数据时,如果需要构建是一个中大型单页应用,会考虑如何更好地在组件外部管理状态,就使用Vuex 。
好处:
①: 能够在 vuex 中集中管理共享的数据,易于开发和后期维护 ②: 可以做状态管理、采用localstorage保存信息、数据一直存储在用户的客户端中 ③: 存储在 vuex 中的数据都是响应式的,能够实时保持数据与页面的同步,能够高效地实现组件之间的数据共享,提高开发效率
vuex核心:
state:vuex的基本数据,数据源存放地,数据是响应式的,用于定义共享的数据。
getter:可以对state进行计算操作,主要用来过滤一些数据,可以在多组件之间复用
mutation:提交更新数据的方法,唯一 一个可以操作state 中数据的方法,必须是同步的,第一个参数是state,第二个参数是commit传过来的数据
action:action是用来处理数据的方法变成异步操作的,一般用来发请求,在 action 中写入函数,然后在页面中用store.dispatch调用,然后在 action 中通过commit 去调用 mutation 通过 mutation 去操作state,进行修改数据。
modules:模块化vuex,可以让每一个模块拥有自己的state、mutation、action、getters,使得结构非常清晰,方便管理
用法
引入vuex
开发依赖 : npm i xxxx --save-dev ; npm i xxxx -D ;
生产依赖: npm i xxxx --save ; npm i xxxx -S; npm i xxxx;
//引入Vue
import Vue from 'vue'
// 引入vuex
import store from '@/store';
//创建Vue实例对象---vm
new Vue({
el:'#app',
render: h => h(App),
// 注册仓库:组件实例身上会多一个属性$store属性
store
})
单独小仓库
import { reqCartList,reqDeleteCartById,reqUpdateCheckedByid } from "@/api";
const state = {
//当前临时用户的购物车的数据
cartList: [],
};
const mutations = {
// 2 赋值给state里面的数据
GETCARTLIST(state,cartList){
// console.log(state,'数据源');
state.cartList = cartList;
}
};
const actions = {
//一定要切记:action第一个参数(minStore) 第二个参数:dispatch的载荷(传递过来的数据)
// 1 获取购物车列表数据
async getCartList({ commit }){
let result = await reqCartList();
// console.log(result.data,123);
if(result.code == 200){
// 转发通知
commit("GETCARTLIST",result.data);
}
},
// 删除购物车某一个产品
async deleteCartListBySkuId({ commit },skuId){
let result = await reqDeleteCartById(skuId);
if (result.code == 200) {
return "ok";
} else {
return Promise.reject(new Error("faile"));
}
},
// 修改购物车某一个产品的选中状态
async updateCheckedById({ commit },{ skuId,isChecked }){
let result = await reqUpdateCheckedByid(skuId, isChecked);
if(result.code == 200){
return "ok";
}else{
return Promise.reject(new Error("faile"));
}
},
//删除全部勾选的产品,因为只有一次性删除一个商品的接口所以这样写,这个action调用另一个action方法多次
deleteAllCheckedCart({ dispatch, getters }) {//context小仓库
//context:小仓库包含commit【提交mutations修改state】 getters【计算属性】 dispatch【派发action】 state【当前仓库数据】属性,action里面都可以拿到
//获取购物车中全部的产品(是一个数组)
let PromiseAll = [];
getters.cartList.cartInfoList.forEach((item) => {
console.log(item);
// 调用其他action方法,返回值是一个promise
let promise = item.isChecked == 1 ? dispatch("deleteCartListBySkuId", item.skuId): "";
//将每一次返回的Promise添加到数组当中
PromiseAll.push(promise);
});
// Promise.all([p1,p2,p3]),p1|p2|p3:每一个都是Promise对象
//只要全部的p1|p2....都成功,返回结果即为成功
//如果有一个失败,返回即为都失败失败结果
return Promise.all(PromiseAll);
},
//修改全部的产品的选中状态的方法
//在action当中如何获取到仓库中的存储的数据
//1:获取到全部购物车的数据【数组:六个元素】
//2:触发updateChecked这个action六次【全部产品都修改了】
//3:updateAllCart,返回一个Promise,告诉组件成功了,还是败了【组件才能继续写别的业务】
updateAllCartIsChecked({ dispatch, state,getters }, isChecked) {
//数组 //遍历购物车里面产品:购物车里面有多少产品,回调就执行多少次
let promiseAll = [];
state.cartList[0].cartInfoList.forEach((item) => {
console.log(item);
let promise = dispatch("updateCheckedById", {
skuId: item.skuId,
isChecked,
});
promiseAll.push(promise);
});
// 另一种写法:可能死循环
// getters.cartList.cartInfoList.forEach(item =>{
// //在当前action内部,调用内部action执行N次
// let promise = dispatch("updateCheckedById", {
// skuId: item.skuId,
// isChecked,
// });
// promiseAll.push(promise);
// })
//如果不书写return,返回结果永远undefined,永远是真
//Promise.all执行,参数需要的是一个数组,数组里面每一个都是promise,如果都成功,promise返回即为成功结果
//如果有一个【修改状态】promise失败返回的结果即为失败
return Promise.all(promiseAll);//可以保证 updateAllCart返回的是promise【成功、失败】
},
}
const getters = {
// 3 简化购物车数据要数据的第0项
cartList(state) {
return state.cartList[0] || {};
},
};
export default {
state,
mutations,
actions,
getters
}
state
组件访问state数据的第一种方式
使用公共数据
在组件中,通过this.$store.state.属性名访问。
在模板中,则可以省略this而直接写成:{{$store.state.属性名}}
组件访问state数据的第二种方式
基于vuex提供的mapState辅助函数,可以方便的把store中指定的数据,映射为当前组件的计算属性
//1.按需导入辅助函数mapState
import { mapState }from 'vuex'
export default {
name: 'left',
computed: {
// 拿到数据
...mapState({
// 右侧需要的是一个函数,当使用这个计算属性的时候,右侧函数立即执行一次
// 注入一个参数state,其实即为大仓库中数据
// categoryList:(state)=>{
// // console.log(state);
// return state.homo.categoryList;
// }
categoryList: state => state.home.categoryList
})
},
}
//页面使用
<div
class="item bo"
v-for="(c1, index) in categoryList"
:key="c1.categoryId"
:class="{ cur: currentIndex == index }"
@mouseenter="changeIndex(index)"
>
mutations修改公共数据
它是Vuex中用来修改公共数据的唯一入口。
在定义时:它的第一个参数是state,第二个参数是载荷commit传过来的数据接收
注意:mutations必须是同步函数,mutation里面不能放异步代码,第二个参数是可选的,表示载荷,是可选的。
**第一种方式:**这里的commit是固定的方法
在调用时:用 this.$store.commit(‘mutation名’, 载荷) 来调用
第二种方式:
在action里面转发通知
const state = {
//当前临时用户的购物车的数据
cartList: [],
};
const mutations = {
//第二个参数接收commit传过来的数据
GETCARTLIST(state,cartList){
// 2 赋值给state里面的数据
// console.log(state,'数据源');
state.cartList = cartList;
}
};
const actions = {
//一定要切记:action第一个参数(minStore) 第二个参数:载荷(传递过去的数据)
// 1 获取购物车列表数据
async getCartList({ commit }){
let result = await reqCartList();
// console.log(result.data,123);
if(result.code == 200){
// 转发通知
commit("GETCARTLIST",result.data);
}
},
}
第三种:mapMutations 辅助函数
基于 Vuex 提供的 mapMutations 辅助函数,可以方便的把 Store 中指定的方法,映射为当前组件的 methods
//1.按需导入mapMutations辅助函数
import { mapMutations } from 'vuex';
export default {
name: 'right',
methods: {
//2.从vuex中把add映射为当前组件的methods方法
//...mapMutations({'新名字': 'mutation名'})
...mapMutations(['add'])
}
}
mutaions拓展理解
问:为啥是$store.commit('mutations的名字')而不是$store.mutations的名字()?
答:Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler) 。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数。
问:数据不可以该在组件内部直接修改吗?
答:不能。虽然语法上不报错,也有响应式的特点。但是不推荐。特别是在严格模式下会报错。若将vue创建 store 的时候传入 strict: true, 开启严格模式,那么任何修改state的操作,只要不经过 mutation的函数,vue就会报错
问:可以传递多个数据吗?
答:参数只能有一个:下面的写法是不对的:
this.$store.commit('setUrl', url, host) // host这个参数将无法被接收到
如果希望传递复杂的数据,第二个参数可以是对象或者数组,例如下面的写法
this.$store.commit('setUrl', { url, host} )
问:等价写法 this.$store.commit({type: 'mutations的名字'})
action
- 作用:发异步请求获取数据,调用mutations来保存数据,将整个ajax操作封装到Vuex的内部
- 要点:
- action 内部可以发异步请求操作,action是间接修改state的,是通过调用 mutation来修改state
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wMbJCaVs-1660634474344)(vuex状态管理.assets/image-20220816151336583.png)]
第一种方式:在组件中通过this.$store.dispatch(‘action名字’,参数)来调用action
mounted() {
this.getDate();
},
methods: {
// 1 获取个人购物车数据
getDate(){
this.$store.dispatch("getCartList");
},
}
第二种方式:基于vuex提供的mapActions辅助函数,可以方便把store中指定的action,映射为当前组件的methods
methods: {
//...mapActions(['actions名']),
...mapActions({'新名字': 'actions名'})
}
getters
作用:它是 Vuex 中的计算属性,当 Store 数据源发生变化时,Getter 的返回值会自动更新,常用来简化数据。
第一种方式:
使用格式
在组件中通过:$store.getters.getter的名字
来访问
第二种方式 :
基于 mapGetters 辅助函数,可以把 store 中的 getter 映射为当前组件的计算属性。
import { mapGetters } from "vuex";
// 获取getters的数据
computed:{
//...mapGetters({'新名字': 'xxx'})
...mapGetters(['categoryView','skuInfo','spuSaleAttrList']),
// 给子组件的数据
skuImageList(){
// console.log(this.skuInfo.skuImageList,'skuImageList');
// 从Getters传递过来的skuInfo上拿放大镜图片数据
//如果服务器数据没有回来,skuInfo这个对象是空对象,防止在undefined身上点数据
return this.skuInfo.skuImageList || [];
}
},
modules
import Vue from 'vue';
import Vuex from 'vuex';
// 需要使用插件一次
Vue.use(Vuex);
// 引入vue的每个小仓库
import home from './home';
import search from './search';
import detail from './detail';
import shopcart from './shopcart'
import user from './user'
import trade from './trade'
// 对外暴露store类的一个实例
export default new Vuex.Store({
// 实现Vuex仓库使用模块式开发存储数据
modules:{
home,
search,
detail,
shopcart,
user,
trade
}
})
modelues拓展理解
问题导入
所有的全局数据、方法都集中在了一起,导致 Vuex 的结构混乱,不利于现阶段的开发和后期的维护
modules的作用
拆分模板,把复杂的场景按模块来拆开
export default new Vuex.Store({
// state: 用来保存所有的公共数据
state: {},
getters: {},
mutations: {},
actions: {},
modules: {
模块名1: {
// namespaced为true,则在使用mutations时,就必须要加上模块名
namespaced: true,
state: {},
getters: {},
mutations: {},
actions: {},
modules: {}
},
模块名2: {
// namespaced不写,默认为false,则在使用mutations时,不需要加模块名
state: {},
getters: {},
mutations: {},
actions: {},
modules: {}
}
}
})
也可以进一步对文件进行拆分
|--store /
|------- index.js # 引入模块
|------- modules
|-------------- / mod1.js # 模块1
|-------------- / mod2.js # 模块2
访问数据和修改数据的调整
- 访问模块中的数据,要加上模块名
获取数据项: {{$store.state.模块名.数据项名}}
获取getters: {{$store.getters['模块名/getters名']}}
访问模块中的mutations/actions:
- 如果namespaced为true,则需要额外去补充模块名
- 如果namespaced为false,则不需要额外补充模块名
$store.commit('mutations名') // namespaced为false
$store.commit('模块名/mutations名') // namespaced为true
小结
使用了modules之后,在访问数据时就要额外添加modules的名字了。
----------- / mod2.js # 模块2
### 访问数据和修改数据的调整
- 访问模块中的数据,要加上模块名
```js
获取数据项: {{$store.state.模块名.数据项名}}
获取getters: {{$store.getters['模块名/getters名']}}
访问模块中的mutations/actions:
- 如果namespaced为true,则需要额外去补充模块名
- 如果namespaced为false,则不需要额外补充模块名
$store.commit('mutations名') // namespaced为false
$store.commit('模块名/mutations名') // namespaced为true
小结
使用了modules之后,在访问数据时就要额外添加modules的名字了。
结论: 在使用modules时,建议都给加上namespaced!