前情回顾:

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的核心有四个部分:

  • state :状态,也可以理解为变量
  • mutation :操作状态的地方
  • action :操作状态之前需要处理异步或业务逻辑
  • getter :对状态进行包装,理解为计算属性computed

也可以参考我之前发过的一篇:

VueX使用简明笔记

本篇还是围绕Vue-Element-Admin项目,针对Vuex状态管理的角度进行简单分析。

这部分也是非常重要的,关乎项目内部业务逻辑、交互逻辑

1、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/tagsView.js小页签状态的定义和操作,下图就是:
    在这里插入图片描述

  • ./modules/user.js重要,用户的状态、状态操作,都在这,比如登录、登出这些逻辑,以及后面会说到的后端校验接口,也是通过它在调用并修改状态

  • ./getters.js对全局需要的一些状态(变量),比如用户信息、动态路由规则、设置等进行集中抽取并暴露。

  • ./index.js是仓库入库,主要完成的工作,是对子仓库模块的引入和注册

2、store入口文件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

3、权限状态控制permission.js

一步步看注释:

// 导入动态、静态路由规则
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
}

4、用户状态控制user.js

// 引入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
}

5、系统错误日志状态控制errLog.js

一步步看注释:

// 定义状态,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,因为在实际工作中,我们可以在这里,拿到错误日志,并且写入数据库或者文件

其他模块就不一一拿出来说了,后面用到,会说明

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐