vuex 序言
鉴于我认识 的小白们不会用vuex,我引用官网对vuex的介绍,以及我整理好的使用方法,详细给大家讲解一下
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
可以理解成是一个单实例,更简单的说像是一个闭合函数,你只能使用外部的变量,内部方法来改变内部的状态,并且返回的内部处理状态,或者你干脆把vue理解成一个大对象,vuex的状态是一直存在的,你随时去用他存好的状态,并且可以跨组件的使用它里面的状态,跳路由也不会影响你存好的状态,只有你调用vuex内部方法才会改变内部状态。好的也许你已经知道他能干嘛了,我开始了解一下vuex跟你的组件之间有什么联系。
以下是一个表示“单向数据流”理念的简单示意:
这是官网上的一张图,我直接引用了,vuex官网教程链接。
这个状态自管理应用包含以下几个部分:
- state,驱动应用的数据源;
- view,以声明方式将 state 映射到视图;
- actions,响应在 view上的用户输入导致的状态变化。
更多关于官网上也详细讲解了 为什么要是用vuex,最后你一定会知道为啥要使用vuex ,
总结:好用 + 好用 + 好用
vuex的应用
光说好用你也不知道咋就那么好用呢?
那我就用两个例子先简单说一下并且告诉你,他的好用之处,首先来了解vuex 的结构,index他的基本结构。
import Vue from "vue";
import Vuex from "vuex";
import apiUrl from "../request/apiUrl";
// 独立的模块
import contacts from "./modules/contacts"; // 联系人
import APIglobal from "@/request/api/global";
// 把所有配置的结构地址,都配置到stroe中
let apiList = {};
function setNewApiList(data) {
for (let key in data) {
if (typeof data[key] === "string") apiList[data[key]] = false;
else setNewApiList(data[key]);
}
}
setNewApiList(apiUrl);
Vue.use(Vuex);
export default new Vuex.Store({
state: {
Loading: { ...apiList }, // 所有接口请求状态
// 全局信息
global_orgclasses: [], // 行业列表
},
getters: {
// 获取对应接口请求状态 true 请求中 false 请求结束
getLoading: (state) => (url) => {
for (let key in state.Loading) {
if (key === url) {
return state.Loading[key];
}
}
return false;
},
},
mutations: {
// 设置loading中对应接口的请求状态
setLoading(state, { url, status }) {
state.Loading[url] = status;
},
// 存储全局用信息
setGloBalInfo(state, payload) {
for (let filed in payload) {
state[`global_${filed}`] = payload[filed]
}
},
},
actions: {
// 触发 getLoading 设置
setLoading({ commit }, data) {
commit("setLoading", data);
},
// 请求行业信息
getOrgclasses({ commit }, callback) {
APIglobal.queryOrgclasses().then(res => {
if (res.result === 0) {
commit("setGloBalInfo", { orgclasses: res.data });
}
callback(res)
}).catch(err => {
callback(err)
})
},
},
modules: {
contacts,
},
});
子模块写法
import APIenterprise from "@/request/api/contacts/enterprise";
const contacts = {
namespaced: true,
// 如果希望你的模块具有更高的封装度和复用性,
// 你可以通过添加 namespaced: true 的方式使其成为带命名空间的模块。
// 当模块被注册后,它的所有 getter、action 及 mutation 都会自动根据模块注册的路径调整命名。
state: () => ({
orgExternalContacts: [], // 外部联系人列表
}),
getters: {
getTitle(state) {
return state.title
}
},
mutations: {
// 存储联系人模块数据
setContactsState(state, payload) {
for (let filed in payload) {
state[`${filed}`] = payload[filed]
}
},
},
actions: {
// 留下一个请求样式
queryExternalContacts({ commit }, callback) {
APIenterprise.queryExternalContacts().then(res => {
if (res.result === 0) {
commit("setContactsState", {
orgExternalContacts: res.data
})
}
})
},
}
}
export default contacts;
如果你看过我之前写过的 vue全局下的loading设置
那么你会有点小熟悉呢,这里面部分代码出现过
- state:单一状态树
- getters:过滤属性
- mutations:同步改变state方法,你必须使用mutations方法改变state
- actions:不能直接修改state,需要借助mutations
- modules:子模块,如果你的模块多了,你就需要了
vuex 中方法的使用
## 1. 大致分为两种类型的使用方法
1-1. 直接使用vuex index 主实例
1-2. 使用modules子模块
#### 1-1 主实例使用方法
使用vuex辅助函数 四大金刚
import { mapState, mapMutations, mapActions, mapGetters } from "vuex";
这样你就可以使用辅助函数去到state中的状态,和getters,以及触发mutation,actions,
如需在modules使用辅助函数 下面会介绍
》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》
mapState / mapGetters 放在computed中,自动计算store中state状态值
computed: {
...mapState({ userid: "userId" }), // 通过别名 userid 调用值
...mapState(["userId"]), // 直接通过 userId 调用
...mapGetters({ meetingTitle: "getTitle" }),
...mapGetters(["getLoading","getName"]),
// 如果你需要通过条件调用,getter中的过滤函数, 直接用this.$store.getters方法去调用
loading() {
// 获取对应接口loading状态,需要传对应接口的接口地址
return (
this.$store.getters.getLoading(this.apiUrl.meeting.list) ||
this.$store.getters.getLoading(this.apiUrl.meeting.delList)
);
},
// 第二种 在需要的地方使用 getLoading(条件)
},
》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》
mapMutations / mapActions
注意啊!mutation 可是不支持异步操作的,action是可以异步触发mutation的
methods: {
...mapMutations([
'increment', // 将 `this.increment()` 映射为 `this.$store.commit('increment')`
// `mapMutations` 也支持载荷:
'incrementBy' // 将 `this.incrementBy(amount)` 映射为 `this.$store.commit('incrementBy', amount)`
]),
...mapMutations({
add: 'increment' // 将 `this.add()` 映射为 `this.$store.commit('increment')`
})
}
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')`
})
}
#### 1-2 子模块使用方法
vuex 文档中详细说明了 这些辅助函数的用法,一看便一目了然
import { createNamespacedHelpers } from "vuex";
const { mapState, mapMutations, mapActions, mapGetters } = createNamespacedHelpers("meeting");
// createNamespacedHelpers(子模块名)
辅助函数的使用方法都是一样的
// this.$store 直接使用 不使用应用的方法
// vuex mutation
this.$store.commit('setLocation',{key1:"mutation"});
// vuex module mutation
this.$store.commit('filter/updateActiveName',"filter/mutation");
// vuex action
this.$store.dispatch('updateLocation',{key2:"action"});
// vuex module actions
this.$store.dispatch('filter/test',"filter/actions");
vuex 例子1讲解
需求:loading状态的需求,boss提出要求因为之前接口都是根据函数传参的方式来设置loading状态的,我们能不能统一管理一下,不要每次都去设置这烦人的loading状态,ok?
- 那么我遇到的第一个问题就是,这么多接口我需要一个地方来存啊
- 然后就是,我得写一个公共方法,去设置这个状态
- 然后你请求了,你还得告诉loading状态我去请求了,怎么搞用watch监听吗?
- 这些问题你都能用方法解决掉,但是操作真的有点复杂了
- 管理状态就成了你最大的困难了吧,你创建单实例然后通过全局混入的方式也可以认真的完成这些问题,问题就在于太繁琐了
不过没关系这些步骤你通通都能在vuex中找到方法
- 首先vuex提同一个统一管理状态的state
- 根据接口地址 getter过滤出你对应接口在state中的状态
- 而且你的组件之间可以随时引用vuex
- 在axios请求中封装好接口请求时,我设置存在state中对应接口状态,数据返回时取消state中的接口状态
- 在computed计算属性的特性下使用接口当前的请求状态
真的崇拜大神们是啥脑回路把这玩意设计的这么好用 哈哈
言归正传啊,vuex主入口上面我已经展示过了,下面我们来看看他们之间的管理状态的流程吧,
在index入口中我已经把所有接口,都存入到state中了
现在你需要的是,如何在请求中触发action的方法改变state的状态
#### 准备程序
## main.js中需要吧vuex挂载到vue实例上
import store from './store';
// 把apiUrl接口地址,全局混入到每个组件中
Vue.mixin({
data() {
return {
apiUrl,
}
}
})
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
## axios请求拦截中 引入store 也就是你的vuex啊
import store from '../store/index';
let instance = axios.create({
// 生产开发环境不固定使用浏览器默认截取到的
// baseURL: process.env.VUE_APP_API_URL, // 域名
timeout: 1000 * 6,
withCredentials: true,
transformResponse: [function transformResponse(data) {
return jsonlint.parse(data)
}],
});
然后在你axios请求中拦截中,以及返回拦截中 使用触发action方法的 dispatch 触发 setLoading 去改变你接口的状态
instance.interceptors.request.use(
config => {
// 省略的很多代码......
store.dispatch("setLoading", {
url: `${config.url}`, // 接口地址
status: true,
});
// 省略的很多代码......
},
error => Promise.error(error)
)
返回拦截中 response ,设置接口的状态false
store.dispatch("setLoading", {
url: `${config.url}`,
status: false,
});
到此为止你已经完成改变state状态中对应接口状态了
现在在你的组件中获取你的接口状态
<template>
<a-spin tip="加载中,请稍后..." :spinning="Loading">
<div>你的内容</div>
</a-spin>
</template>
<script>
export default {
computed: {
// 加载状态
Loading() {
return (
this.$store.getters.getLoading(this.apiUrl.account) || false
// this.apiUrl 是我在main.js混入接口,所以以后的每个子组件中都可以直接访问this.apiUrl里的所有接口状态
);
},
},
methods:{
APi接口方法,请求触发你封装好的改变状态的方法,
如果请求中接口状态一直是true,
直到接口返回或者发生错误接口状态改变为false
}
}
<script>
好了到这里你就完成例子1的需求了
vuex 例子2讲解
因为时间原因我明天继续改进,更新出第二个例子,如果你觉得有帮助了,帮忙点赞收藏谢谢各位前端小伙伴了