阅读 2218

打造vuecli3+element后台管理系统(三)优化路由和vuex仓库,给router和store分模块

我们在做后台系统的时候,经常会有比较多的功能页面,每个页面或多或少都会有需要存储在vuex中的数据,一般情况下我们会在state下定义不同的object,但是当功能一多,全部字段和action、mutations都定义在一个文件里,难免会使文件变得很难维护。全部页面的路由都定义在一个路由文件里也会使文件代码变得比较多,后期维护麻烦。所以我们应该将仓库和路由“模块化”,不同功能定义不同的模块文件,最后统一在index文件引入。

一、路由分模块

1.1 创建router文件夹,存放路由定义文件

  • 创建modules文件夹,主要存放功能页面路由定义文件
  • 创建index.js文件,定义路由
1.1-1 @/router/index.js

router/index.js主要功能是:

  1. 定义常规路由,即那些不需要权限就可访问的页面,比如说登录注册、后台主页、404页面等。
  2. 引入需要根据权限加载的路由模块
  3. 定义创建路由、重置路由方法
// index.js
import Vue from 'vue'
import Router from 'vue-router'

Vue.use(Router)
// 定义常规路由
export const constantRoutes = [
  {
    path: '/redirect',
    component: () => import('@/layout'),
    hidden: true,
    children: [
      {
        path: '/redirect/:path*',
        component: resolve => void require(['@/views/redirect/index'], resolve)
      }
    ]
  },
  {
    path: '/',
    redirect: '/login'
  },
  {
    path: '/login',
    name: 'Login',
    component: resolve => void require(['@/views/login/index'], resolve),
    hidden: true
  },
  {
    path: '/register',
    name: 'Register',
    component: resolve => void require(['@/views/login/register'], resolve),
    hidden: true
  },
  {
    path: '/resetPsw',
    name: 'ResetPsw',
    component: resolve => void require(['@/views/login/resetPsw'], resolve),
    hidden: true
  },

  {
    path: '/404',
    component: () => import('@/views/404'),
    hidden: true
  }
]

/** 权限路由
 * 是动态的路由、需要根据权限加载的路由
 */
const modulesFiles = require.context('./modules', true, /\.js$/)
const routesModules = []
// 自动引入modules目录下的所有模块
modulesFiles.keys().reduce((modules, modulePath) => {
  const value = modulesFiles(modulePath)
  routesModules.push(value.default)
}, {})
export const asyncRoutes = routesModules

/** 404路由
 * 最终无法匹配到相应路由,重定向到404
 * 异步加载路由时,在生成完异步路由准备挂载时,需要将重定向404的匹配规则定义在最后面,否则刷新会出错。
 */
export const notFoundRoutes = [
  {
    path: '*',
    redirect: '/404',
    hidden: true,
    meta: {
      title: '404'
    }
  }
]

// 定义实例化路由的方法
const createRouter = () => new Router({
  // mode: 'history', // require service support
  scrollBehavior: () => ({ y: 0 }),
  routes: constantRoutes // 挂载常规路由
})
// 实例化路由
const router = createRouter()

// 定义实重置路由的方法
export function resetRouter() {
  const newRouter = createRouter()
  router.matcher = newRouter.matcher // reset router
}

export default router
复制代码

二、vuex仓库分模块

和router模块一样,咱也需要给store模块创建一个文件夹,在里头存放定义文件

2.1 创建store文件夹,存放vuex定义文件

  • 创建modules文件夹,主要存放各模块下的仓库定义文件
  • 创建index.js文件,初始化vuex实例
  • 使用vuex持久化插件vuex-persistedstate,让你的数据可以实现刷新也不丢失噢~
2.1-1 @/store/index.js

该文件声明了vuex实例,同时批量引入modules下的store文件,也声明了vuex-persistedstate插件实例。

关于persistedState的初始化,注意vuex中的参数不是全部都要持久化,你可以根据业务逻辑自行定义需要持久化的参数

import Vue from 'vue'
import Vuex from 'vuex'
import getters from './getters'
import createPersistedState from 'vuex-persistedstate'

Vue.use(Vuex)

// (创建了)一个包含了modules文件夹(包含子目录)下面的,所有文件名以 `.js` 结尾的、能被 require 请求到的文件的上下文。
const modulesFiles = require.context('./modules', true, /\.js$/)
// keys() 方法用于从modules创建一个包含modules里键值的可迭代对象。
const modules = modulesFiles.keys().reduce((modules, modulePath) => {
  // 模块名,取文件名
  const moduleName = modulePath.replace(/^\.\/(.*)\.\w+$/, '$1')
  // 获取键名为modulePath的文件内容
  const value = modulesFiles(modulePath)
  // 将文件中的默认导出模块 赋值给迭代对象modules
  modules[moduleName] = value.default
  // 返回迭代对象modules
  return modules
  // 默认值是空对象{}
}, {})

// 创建仓库实例
const store = new Vuex.Store({
  modules,
  getters,
  // 使用持久化插件
  plugins: [
    // 存储vuex状态,使之刷新不丢失
    createPersistedState({
      // 这里我用的是localStorage,你也可以改成sessionStorage,cookie也行不过我没研究嘻嘻嘻
      storage: window.localStorage,
      reducer(val) {
        return {
          // 将要存储的state中的值放在这里
          user: {
            name: val.user.name
          }
        }
      }
    })
  ]
})

export default store

复制代码

那我们就来看看咋使用,我们可以写个userjs来试试,里头定义登录登出的action,因为登录登出可能不止一个地方会运用到,而且登录登出可能会涉及到state里头好几个状态值的改变,所以我就把登录登出的逻辑写在action里头,这样分发的时候也可以改变状态值:

2.1-2 modules目录下新建user.js,用来存放用户信息,定义登陆、登出的action
import API from '@/assets/http/apiUrl'
import Request from '@/assets/http'

const user = {
  state: {
    token: '',
    name: ''
  },
  mutations: {
    SET_TOKEN: (state, data) => {
      // 先初步定义token和username这两个值
      state.token = data
      localStorage.setItem('ADMIN_TOKEN', data)
    },
    SET_NAME: (state, data) => {
      state.name = data
    }
  },
  actions: {
    // 登陆
    Login({ commit }, params) {
      return new Promise((resolve, reject) => {
        Request.httpRequest({
          method: 'post',
          url: API.Login,
          params: params,
          success: data => {
            commit('SET_TOKEN', data.token)
            resolve(data)
          },
          error: err => {
            reject(err)
          }
        })
      })
    },

    // 短信登录,这里为了方便我就直接复用登录的action了,正式项目中肯定要调用另外的接口
    LoginByVin({ dispatch, commit }, params) {
      return dispatch('Login', params)
    },

    // 重置token和state值
    ResetToken({ commit }) {
      return new Promise(resolve => {
        commit('SET_TOKEN', '')
        commit('SEI_NAME', '')
        localStorage.removeItem('ADMIN_TOKEN')
        resolve()
      })
    },

    // 登出
    LogOut({ dispatch, commit }) {
      return dispatch('ResetToken')
    }
  }
}

export default user

复制代码

新建之后不需要在index那里引入哦,因为我们已经写了批量引入的逻辑啦~是不是很方便呢

然后就是使用了,关于Login,在登陆页面只需要分发action就行:

params = {
    'username': this.loginForm.user,
    'password': this.loginForm.password
}
this.$store.dispatch('Login', params).then(() => {
    this.loading = false
    this.$router.push({ path: this.redirect || '/home' })
}).catch(() => {
    this.loading = false
})
复制代码

具体代码逻辑可以下载项目来看一下下