前情回顾:
vue-element-admin项目学习笔记(1)安装、配置、启动项目
vue-element-admin项目学习笔记(2)main.js
文件分析
vue-element-admin项目学习笔记(3)路由分析一:静态路由
vue-element-admin项目学习笔记(4)路由分析二:动态路由及permission.js
vue-element-admin项目学习笔记(5)路由分析三:动态路由匹配逻辑
关于什么是vuex?和理论部分,本篇不做讨论,假设前提是,你已经学习和掌握了vue基础的小伙伴。
vuex的核心有四个部分:
也可以参考我之前发过的一篇:
VueX使用简明笔记
本篇还是围绕Vue-Element-Admin项目,针对Vuex状态管理的角度进行简单分析。
这部分也是非常重要的,关乎项目内部业务逻辑、交互逻辑
Vue-Element-Admin项目的store仓库位置在项目目录/src/store
这个路径下。
结构如下:
modules目录
内主要存放的一些自定义的子仓库模块,你自己写的,最好也按照规范存到这里
./modules/app.js
存储和操作cookie、语言切换、默认尺寸等状态,理解为全局变量也行
./modules/errLog.js
定义和操作项目错误日志状态
./modules/permission.js
权限状态的定义和操作,这个在动态路由计算那篇中,已经详细聊过了
./modules/settings.js
系统设置状态的定义和操作,包括主题等
./modules/user.js
重要,用户的状态、状态操作,都在这,比如登录、登出这些逻辑,以及后面会说到的后端校验接口,也是通过它在调用并修改状态
./getters.js
对全局需要的一些状态(变量),比如用户信息、动态路由规则、设置等进行集中抽取并暴露。
./index.js
是仓库入库,主要完成的工作,是对子仓库模块的引入和注册
一步步看注释:
// 引入vue
import Vue from 'vue'
// 引入vuex
import Vuex from 'vuex'
// 引入getters 理解为全局变量
import getters from './getters'
// 使用vuex,因为本质上是插件
Vue.use(Vuex)
// 在modules目录下读取所有模块文件名
const modulesFiles = require.context('./modules', true, /\.js$/)
// 过滤匹配模块名:值 返回modules
const modules = modulesFiles.keys().reduce((modules, modulePath) => {
const moduleName = modulePath.replace(/^\.\/(.*)\.\w+$/, '$1')
const value = modulesFiles(modulePath)
modules[moduleName] = value.default
return modules
}, {})
// 注册仓库模块
const store = new Vuex.Store({
modules,
getters
})
// 暴露
export default store
一步步看注释:
// 导入动态、静态路由规则
import { asyncRoutes, constantRoutes } from '@/router'
// 具体路由规则与用户roles匹配的工具函数,被filterAsyncRoutes调用
// 返回布尔值
function hasPermission(roles, route) {
// 规则有元信息 且 元信息 有roles项
if (route.meta && route.meta.roles) {
// 开始比对,role在不在route.meta.roles中,返回比对结果
return roles.some(role => route.meta.roles.includes(role))
} else {
//没有元信息,可以返回真,说明没有配置权限,可以访问
return true
}
}
// 遍历路由规则与用户roles匹配的工具函数
export function filterAsyncRoutes(routes, roles) {
const res = []
// 遍历传过来的所有动态路由规则进行遍历
routes.forEach(route => {
// tmp 每一个规则
const tmp = { ...route }
// 调用hasPermission方法(布尔值),进行匹配
if (hasPermission(roles, tmp)) {
// 子级路由递归计算
if (tmp.children) {
tmp.children = filterAsyncRoutes(tmp.children, roles)
}
res.push(tmp)
}
})
return res
}
// 两个状态、数组routes、addRoutes
// routes 当前用户所有路由
// addRoutes 当前用户动态路由。权限计算出来的
// 通过浏览器vue插件可以观察看到
const state = {
routes: [],
addRoutes: []
}
// mutations操作state
// 给这两个数组赋值
const mutations = {
SET_ROUTES: (state, routes) => {
state.addRoutes = routes
state.routes = constantRoutes.concat(routes)
}
}
// actions操作异步
// generateRoutes方法,计算生成权限动态路由
const actions = {
generateRoutes({ commit }, roles) {
return new Promise(resolve => {
// 定义一个临时变量
let accessedRoutes
// 当前用户的roles中是否包含admin
if (roles.includes('admin')) {
// 包含的话,就把所有动态路由规则给它,asyncRoutes是所有动态路由,导入的
// admin用户不用计算
accessedRoutes = asyncRoutes || []
} else {
// 否则在所有动态路由规则中进行过滤匹配
accessedRoutes = filterAsyncRoutes(asyncRoutes, roles)
}
// 提交修改数组
commit('SET_ROUTES', accessedRoutes)
resolve(accessedRoutes)
})
}
}
export default {
namespaced: true,
state,
mutations,
actions
}
// 引入src/api下的user接口,登录login, 退出登录logout, 获取用户信息getInfo
import { login, logout, getInfo } from '@/api/user'
// 引入src/utils/auth下的三个方法:获取getToken, 设置setToken, 删除removeToken
import { getToken, setToken, removeToken } from '@/utils/auth'
// 引入路由以及resetRouter重置路由方法,重置是因为切换用户/切换登录,需要重置
import router, { resetRouter } from '@/router'
// 定义用户信息(数据、状态)
const state = {
token: getToken(),
name: '',
avatar: '',
introduction: '',
roles: []
}
// mutations是改变状态的唯一途径
// 改变用户信息的一些操作
const mutations = {
SET_TOKEN: (state, token) => {
state.token = token
},
SET_INTRODUCTION: (state, introduction) => {
state.introduction = introduction
},
SET_NAME: (state, name) => {
state.name = name
},
SET_AVATAR: (state, avatar) => {
state.avatar = avatar
},
SET_ROLES: (state, roles) => {
state.roles = roles
}
}
// 异步操作
const actions = {
// 用户登录
login({ commit }, userInfo) {
const { username, password } = userInfo
return new Promise((resolve, reject) => {
login({ username: username.trim(), password: password }).then(response => {
const { data } = response
commit('SET_TOKEN', data.token)
setToken(data.token)
resolve()
}).catch(error => {
reject(error)
})
})
},
// get user info
getInfo({ commit, state }) {
return new Promise((resolve, reject) => {
getInfo(state.token).then(response => {
const { data } = response
if (!data) {
reject('Verification failed, please Login again.')
}
const { roles, name, avatar, introduction } = data
// roles must be a non-empty array
if (!roles || roles.length <= 0) {
reject('getInfo: roles must be a non-null array!')
}
commit('SET_ROLES', roles)
commit('SET_NAME', name)
commit('SET_AVATAR', avatar)
commit('SET_INTRODUCTION', introduction)
resolve(data)
}).catch(error => {
reject(error)
})
})
},
// user logout
logout({ commit, state, dispatch }) {
return new Promise((resolve, reject) => {
// 先调了退出登录的接口logout,传入token,后端作废处理后返回
logout(state.token).then(() => {
commit('SET_TOKEN', '')//清理仓库中的token
commit('SET_ROLES', [])//清理仓库中的权限
removeToken()//移除cookie中的token
resetRouter()//重置路由,清理为当前用户计算的动态路由规则
// 清空tagsView(就是打开页面的小标签)
dispatch('tagsView/delAllViews', null, { root: true })
resolve()
}).catch(error => {
reject(error)
})
})
},
// remove token
resetToken({ commit }) {
return new Promise(resolve => {
commit('SET_TOKEN', '')
commit('SET_ROLES', [])
removeToken()
resolve()
})
},
// dynamically modify permissions
async changeRoles({ commit, dispatch }, role) {
const token = role + '-token'
commit('SET_TOKEN', token)
setToken(token)
const { roles } = await dispatch('getInfo')
resetRouter()
// generate accessible routes map based on roles
const accessRoutes = await dispatch('permission/generateRoutes', roles, { root: true })
// dynamically add accessible routes
router.addRoutes(accessRoutes)
// reset visited views and cached views
dispatch('tagsView/delAllViews', null, { root: true })
}
}
export default {
namespaced: true,
state,
mutations,
actions
}
一步步看注释:
// 定义状态,logs数组
const state = {
logs: []
}
// 定义两个改变状态的方法
// ADD_ERROR_LOG 添加日志
// CLEAR_ERROR_LOG 清除
const mutations = {
ADD_ERROR_LOG: (state, log) => {
state.logs.push(log)
},
CLEAR_ERROR_LOG: (state) => {
state.logs.splice(0)
}
}
// 定义两个action方法,同上,commit到mutation中执行改变
const actions = {
addErrorLog({ commit }, log) {
commit('ADD_ERROR_LOG', log)
},
clearErrorLog({ commit }) {
commit('CLEAR_ERROR_LOG')
}
}
export default {
namespaced: true,
state,
mutations,
actions
}
为什么要单独说一下errLog.js,因为在实际工作中,我们可以在这里,拿到错误日志,并且写入数据库或者文件
其他模块就不一一拿出来说了,后面用到,会说明