以下为vue后台管理项目中使用vuex的一点总结,截取了其中部分代码,如有什么错误,还望指出。
1. token 存储 登陆成功之后,需要把获取到的 token 存储到 vuex 中,配合 axios(或其他 ajax 库)的拦截器每次请求前加到 header 中传给后台。不过光放在 vuex 是不行的,因为考虑到浏览器会刷新,当用户手动刷新了浏览器之后,vuex 中的状态就会重置。所以当刷新页面之后,由于 token 丢失,后台接收不到 token 会返回 401 到前台,而前台检测到状态 401 则会跳转到登录页,所以每次刷新就会回到登录每次刷新就会回到登录。 为了解决这个问题,要配合:本地存储 localStorage 或者 sessionStorage
对象的 get, set 属性。
/* state.js */ export default { get UserToken() { return sessionStorage.getItem("token"); }, set UserToken(value) { sessionStorage.setItem("token", value); } } /* mutation.js */ export default { LOGIN_IN(state, token){ state.UserToken = token; }, LOGIN_OUT(state){ state.UserToken = null; } } /* axios response拦截器 */ instance.interceptors.response.use(function (response) { return response.data; }, function (error) { if (error.response.status == 401){ Message({ type: "warning", message: "授权失败,请重新登录" }); store.commit("LOGIN_OUT"); router.replace({path: \'/login\'}) }else{ return Promise.reject(error); } }); 可以发现,这样一来,虽然我们从 vuex 中取数据,但实际上我们操作的都是 sessionStorage。由于 sessionStorage 在浏览器关闭之前都是有效的,即使是刷新了,还是能从 sessionStorage 获取到 token,从而防止了刷新回到登陆的问题。 2. 用户权限 登陆之后会从后台获取到当前用户的权限数组,同样的我们也是存储到 vuex 里,不过这个就不需要什么 get set 了,直接存就可以了。 /* state.js */ export default { permissionList: null } /* menuNav.vue */ <template> <div> <ul> <li v-for="item in permissionList">{{item.name}}</li> </ul> </div> </template> <script> export default { computed: { permissionList() { return this.$store.state.permissionList; } } } </script> 权限有2个问题需要考虑:
刷新后vuex存放的权限数组丢失;
用户手动输入地址进入没有权限的路由,比如没有 /admin 路由的用户在浏览器修改了路由为 /admin;
对于第一个问题,可以在每个路由进入前判断是否存在权限数组,
如果没有:则去获取;
如果有:就进行第二个问题的判断。
为此,我们需要对在路由的配置时,标识路由是否需要权限,如果需要,并且当前用户没有权限,则不让其进入。
由于后台返回的权限数组很可能是存在二级,三级,为了方便用数组进行权限判断,可以配合store.getters,用递归的方式将其转化为一维数组并存到store.getters中,在进入每个路由前通过Array.includes来判断是否存在当前权限。
router.beforeEach((to, from, next) => { /* 判断路由是否需要登录,如果需要且无token回到登录 */ if (to.matched.some(record => record.meta.requiresAuth)) { if (!store.state.UserToken) { next({ path: \'/login\' }) } else { /* 如果没有权限列表先获取 */ if (!store.state.permissionList) { store.dispatch("fetchPermissionList", next()); } else { var userAllPermission = store.getters.userAllPermission; /* 判断是否存在*/ var isExist = userAllPermission.includes((item) => { return to.name==item.name }); /* 有当前路由的权限才进入,否则404 */ if (isExist) { next(); } else { next({path:\'/404\'}); } } } } else { /* 除了登录页,无需权限的都可进入 */ if(to.path!="/login"){ next(); }else{ /* 有token不再进去登录页,回到之前的页面 */ if(store.state.UserToken){ next(from.fullPath); } } } }) 当然,也可以通过this.$router.addRoutes来动态添加路由。 3. 多个页面通用数据 项目中通常会有一些各个页面共用的数据,比如省份城市列表。为了减少请求,城市都是等到点击对应的省份时才去获取的。 类似这种东西,就可以放到vuex中来维护,页面都共享一份数据,这样一来无疑可以减少一些不必要的请求。否则,如果每个页面都存一份单独的数据,假设A页面请求了广东省的城市,B,C页面需要时,由于每个页面的数据是单独的,B,C又分别需要去请求一次。而放到vuex来维护,则只需要请求一次就可以共享了。 /* state.js */ export default { province:[] } /* mutation */ export default { SET_PROVINCE(state, provincelist){ state.province = provincelist; } } /* action.js */ export default { /* 获取所有省份 */ fetchProvinceList({ commit, state, }) { axios .get(`/cityRegions`) .then(res => { if (res.status == 0) { /* 组装element-ui组件需要的格式 */ let province = res.data.map(item => { return { value: item.id, label: item.regionName, children: [] }; }); commit("SET_PROVINCE", province); } }) }, /* 获取省份下的城市 */ fetchCityList({ commit, state, }, id) { /* 找到当前点击的省份 */ var parent = state.province.find(item => { return item.value == id; }); /* 如果已经有子级城市就不再请求 */ if (parent.children && parent.children.length <= 0) { axios .get(`/cityRegions?pId=${id}`) .then(res => { if (res.status == 0) { var city = res.data; city = city.map(item => { return { value: item.id, label: item.regionName }; }); parent.children = city.length > 0 ? city : null; } }) } } }