First tell the students reading this article my level is limited, rookie a :smile:.

  • After sharing some small details in the official vUE document last time, many big guys said that the basic operation, not deep enough, look at the source code
  • In fact, I am just a rookie, to share their own learning process, the source code can not explain, but recently just look at
  • Vuex (mutation, getters, action, etc.) is a vuex application from an open source framework.

Tip: This article is not for beginners who have never used Vuex before, or those who are already proficient in using vuex on large projects

First of all, I share the source code based on an open rapid development platform. I spent about 2 days specially sorting out the Vuex application mode in its front-end project. But I will tell you a funny thing first, you will be very interested.

First of all, I didn’t choose this framework… , I was just chosen to fill in the hole, of course I don’t mind, the framework open source for 4 months, but the official documentation from the beginning to now is almost impossible to ridicule the “concise”, so the development process is basically their own source code.

One day the framework issued last month a small version, but there is an obvious links in the version log is put in wrongly, the after repair the problem can’t find the correct use of the link, I for the framework of documents between rough experience the already a long time and I am concerned, I made a post in their BBS and ridicule version log link is wrong. Results The next day:

Well, the end of the story, or happy to talk about the code!

First of all, you can take a look at the cover picture (the cover picture has been replaced and inserted in the article). It is my summary of the vuEX application structure of the project, and we will explain it point by point.

The index module

I don’t have to say that main introduces store, but it’s important to note that this is where the path is introduced

//main.js

import store from './store/'
Copy the code

import app from './modules/app'
import user from './modules/user'
import permission from './modules/permission'
import getters from './getters'

Vue.use(Vuex)

export default new Vuex.Store({
  modules: {
    app,
    user,
    permission
  },
  getters
})
Copy the code

This part of the content is very simple, store is divided into several modules, in the index unified export for global call.

Module (Store module)

I suggest you look at this section against my cover art

Js file, divided into app.js (global state configuration), Permissibility. Js (storing routing, including actions for routing), user.js (storing user information, Contains the login interface and the login processing personal information actions), combined with the code respectively to introduce the contents of the inside application:

app.js

import Vue from 'vue'
import {
  SIDEBAR_TYPE,
  DEFAULT_THEME,
  DEFAULT_LAYOUT_MODE
} from "@/store/mutation-types"

const app = {
  state: {
    sidebar: {
      opened: true.withoutAnimation: false
    },
    device: 'desktop'.theme: ' '.layout: ' '.contentWidth: ' '.fixedHeader: false.fixSiderbar: false.autoHideHeader: false.color: null.weak: false.multipage: false // The default multi-tab mode
  },
  mutations: {
    SET_SIDEBAR_TYPE: (state, type) = > {
      // Open system sidebar as expanded or collapsed
      state.sidebar.opened = type
      // After changing the state state, use localstorage to store variables corresponding to state.
      // Solve the vuEX status refresh problem
      Vue.ls.set(SIDEBAR_TYPE, type)//ls indicates the vue-ls mode, which is equivalent to setLocalstorage
    },
    CLOSE_SIDEBAR: (state, withoutAnimation) = > {
      Vue.ls.set(SIDEBAR_TYPE, true)
      state.sidebar.opened = false
      state.sidebar.withoutAnimation = withoutAnimation
    },
    TOGGLE_DEVICE: (state, device) = > {
      state.device = device
    },
    TOGGLE_THEME: (state, theme) = > {
      // setStore('_DEFAULT_THEME', theme)Vue. Ls. Set (DEFAULT_THEME, theme) state. The theme = · · · · · · · theme}},actions: {
  	// Action is an asynchronous operation on mutations, which is very simple
    setSidebar: ({ commit }, type) = > {
      // trigger mutations SET_SIDEBAR_TYPE
      commit('SET_SIDEBAR_TYPE', type)
    },
    CloseSidebar({ commit }, { withoutAnimation }) {
      commit('CLOSE_SIDEBAR', withoutAnimation)
    },
    ToggleDevice({ commit }, device) {
      commit('TOGGLE_DEVICE', device)
    },
    ToggleTheme({ commit }, theme) {
      commit('TOGGLE_THEME', theme)} ······}}export default app

Copy the code

There are three common parts of state, mutations and Actions in this file, and the file of mutation-types is referenced at the beginning, which stores only some uniform string variable names, and the key names used for storage in localstorage, which are almost all in uppercase, can be posted first. The following will not be explained separately:

export const ACCESS_TOKEN = 'Access-Token'
export const SIDEBAR_TYPE = 'SIDEBAR_TYPE'
export const DEFAULT_THEME = 'DEFAULT_THEME'
export const DEFAULT_LAYOUT_MODE = 'DEFAULT_LAYOUT_MODE'
export const DEFAULT_COLOR = 'DEFAULT_COLOR'
export const DEFAULT_COLOR_WEAK = 'DEFAULT_COLOR_WEAK'
export const DEFAULT_FIXED_HEADER = 'DEFAULT_FIXED_HEADER'
export const DEFAULT_FIXED_SIDEMENU= 'DEFAULT_FIXED_SIDEMENU'
export const DEFAULT_FIXED_HEADER_HIDDEN = 'DEFAULT_FIXED_HEADER_HIDDEN'
export const DEFAULT_CONTENT_WIDTH_TYPE = 'DEFAULT_CONTENT_WIDTH_TYPE'
export const DEFAULT_MULTI_PAGE = 'DEFAULT_MULTI_PAGE'
export const USER_NAME = 'Login_Username'
export const USER_INFO = 'Login_Userinfo'
export const USER_AUTH = 'LOGIN_USER_BUTTON_AUTH'
export const SYS_BUTTON_AUTH = 'SYS_BUTTON_AUTH'

Copy the code

Each state is almost the same, and only a simple comment will be made in the first variable or method of each object. It should be easy to understand with vuex application foundation. Take a look at the call to SET_SIDEBAR_TYPE:

mounted () {
	// Call SET_SIDEBAR_TYPE to read the value of SIDEBAR_TYPE in localStorage
	store.commit('SET_SIDEBAR_TYPE', Vue.ls.get(SIDEBAR_TYPE, true))}Copy the code

The idea is relatively simple. When storing all the states to be stored in localstorage, the unified string name corresponding to the system is used to create key-value pair storage, and the mutations method that operates on the corresponding state is also updated in localstorage.

permission.js

Permission stores routing information and methods for manipulating routes based on roles. The framework uses a front-end to store only basic routes.

//router.config contains basic routes
import { asyncRouterMap, constantRouterMap } from "@/config/router.config"

/** * Filters whether the account has a certain permission and removes the menu from the load list */
function hasPermission(permission, route) {
  if (route.meta && route.meta.permission) {
    let flag = - 1
    for (let i = 0, len = permission.length; i < len; i++) {
      flag = route.meta.permission.indexOf(permission[i])
      if (flag >= 0) {
        return true}}return false
  }
  return true
}

/** * If one account has multiple roles, use this method to filter menus that do not exist */
// eslint-disable-next-line
function hasRole(roles, route) {
  if (route.meta && route.meta.roles) {
    return route.meta.roles.indexOf(roles.id)
  } else {
    return true}}function filterAsyncRouter(routerMap, roles) {
  const accessedRouters = routerMap.filter(route= > {
    if (hasPermission(roles.permissionList, route)) {
      if (route.children && route.children.length) {
        route.children = filterAsyncRouter(route.children, roles)
      }
      return true
    }
    return false
  })
  return accessedRouters
}


const permission = {
  state: {
    routers: constantRouterMap,// Base routing
    addRouters: []// Remaining routes to be added based on user roles
  },
  mutations: {
    SET_ROUTERS: (state, data) = > {
    // Add routes and merge complete routes to the routers variable. The routes loaded in actual applications are routers
      state.addRouters = data
      state.routers = constantRouterMap.concat(data)
    }
  },
  actions: {
    GenerateRoutes({ commit }, data) {
      return new Promise(resolve= > {
        const { roles } = data
        let accessedRouters = filterAsyncRouter(asyncRouterMap, roles)
        resolve()
      })
    },
    // Add the route to the main interface dynamically
    UpdateAppRouter({ commit }, routes) {
      return new Promise(resolve= > {
        let routelist = routes.constRoutes;
        resolve()
      })
    }
  }
}

export default permission
Copy the code

For routing operations, SET_ROUTERS in mutations are not directly called, but the asynchronous UpdateAppRouter is used to update:

let constRoutes = [];
  constRoutes = generateIndexRouter(menuData);
  // Add a route to the main interface
  store.dispatch('UpdateAppRouter',  { constRoutes }).then((a)= > {
    // Generate an accessible routing table based on roles permissions
    // Dynamically add the accessible routing table
    router.addRoutes(store.getters.addRouters)
    const redirect = decodeURIComponent(from.query.redirect || to.path)
    if (to.path === redirect) {
      // The hack method ensures that addRoutes is completenext({ ... to,replace: true})}else {
      // Redirect to the destination route
      next({ path: redirect })
    }
  })
Copy the code

Router addRoutes = router addRoutes = router addRoutes = router addRoutes = router addRoutes = router addRoutes = router addRoutes It will not be explained separately:

import Vue from 'vue'
import { USER_INFO} from "@/store/mutation-types"
const getters = {
  device: state= > state.app.device,
  theme: state= > state.app.theme,
  color: state= > state.app.color,
  token: state= > state.user.token,
  avatar: state= > {state.user.avatar = Vue.ls.get(USER_INFO).avatar; return state.user.avatar},
  username: state= > state.user.username,
  nickname: state= > {state.user.realname = Vue.ls.get(USER_INFO).realname; return state.user.realname},
  welcome: state= > state.user.welcome,
  permissionList: state= > state.user.permissionList,
  userInfo: state= > {state.user.info = Vue.ls.get(USER_INFO); return state.user.info},
  addRouters: state= > state.permission.addRouters// Get the route owned by the user
}

export default getters
Copy the code

Getters is simpler, fetching variables from state in each module.

The entire routing operation is clear:

  1. Call the routeState asynchronous method UpdateAppRouter to SET_ROUTERS and set the addRouters and routers variables in the state for the routers.
  2. Then use the vueRouter’s router.addRoutes (‘ Route to be added ‘) method to complete the route. The store’s getters method takes the addRouters variable from the route.

user.js

User.js mainly stores the login of users and the processing of token and user information in VUex after login. I think this part can be used for reference in many people’s projects, and the complete code is put here:

import Vue from 'vue'
import { login, logout } from "@/api/login" // The login and logout API calls methods
import { ACCESS_TOKEN, USER_NAME,USER_INFO,USER_AUTH,SYS_BUTTON_AUTH } from "@/store/mutation-types"
import { welcome } from "@/utils/util"

const user = {
  state: {
    token: ' './ / the user token
    username: ' '.realname: ' '.welcome: ' '.avatar: ' '.permissionList: [].info: {}},mutations: {
    SET_TOKEN: (state, token) = > {
      / / setToken in the state
      state.token = token
    },
    SET_NAME: (state, { username, realname, welcome }) = > {
      state.username = username
      state.realname = realname
      state.welcome = welcome
    },
    SET_AVATAR: (state, avatar) = > {
      state.avatar = avatar
    },
    SET_PERMISSIONLIST: (state, permissionList) = > {
      state.permissionList = permissionList
    },
    SET_INFO: (state, info) = > {
      state.info = info
    },
  },

  actions: {
    / / login
    Login({ commit }, userInfo) {
      return new Promise((resolve, reject) = > {
        // Invoke the login interface
        login(userInfo).then(response= > {
          if(response.code =='200') {const result = response.result
            const userInfo = result.userInfo
            // Store token information in localstorage and state. Of course, localstorage sets the validity period
            Vue.ls.set(ACCESS_TOKEN, result.token, 7 * 24 * 60 * 60 * 1000)
            Vue.ls.set(USER_NAME, userInfo.username, 7 * 24 * 60 * 60 * 1000)
            Vue.ls.set(USER_INFO, userInfo, 7 * 24 * 60 * 60 * 1000)
            commit('SET_TOKEN', result.token)
            commit('SET_INFO', userInfo)
            commit('SET_NAME', { username: userInfo.username,realname: userInfo.realname, welcome: welcome() })
            commit('SET_AVATAR', userInfo.avatar)
            resolve(response)
          }else{
            reject(response)
          }
        }).catch(error= > {
          reject(error)
        })
      })
    },
    // Get user information
    GetPermissionList({ commit }) {
      return new Promise((resolve, reject) = > {
        let v_token = Vue.ls.get(ACCESS_TOKEN);
        let params = {token:v_token};
        queryPermissionsByUser(params).then(response= > {
          const menuData = response.result.menu;
          const authData = response.result.auth;
          const allAuthData = response.result.allAuth;
          //Vue.ls.set(USER_AUTH,authData);
          sessionStorage.setItem(USER_AUTH,JSON.stringify(authData));
          sessionStorage.setItem(SYS_BUTTON_AUTH,JSON.stringify(allAuthData));
          if (menuData && menuData.length > 0) {
            commit('SET_PERMISSIONLIST', menuData)
          } else {
            reject('getPermissionList: permissions must be a non-null array ! ')
          }
          resolve(response)
        }).catch(error= > {
          reject(error)
        })
      })
    },

    / / logout
    Logout({ commit, state }) {
      return new Promise((resolve) = > {
        let logoutToken = state.token;
        // Clear token information in state and localstorage
        commit('SET_TOKEN'.' ')
        commit('SET_PERMISSIONLIST', [])
        Vue.ls.remove(ACCESS_TOKEN)
        //console.log('logoutToken: '+ logoutToken)
        logout(logoutToken).then((a)= > {
          resolve()
        }).catch((a)= > {
          resolve()
        })
      })
    },

  }
}

export default user

Copy the code

Then let’s look at the code call to it:

 import {mapActions} from "vuex"...... the methods: {... mapActions(["Login"."Logout"]), · · · · · · handleSubmit () {let that = this
        let loginParams = {
          remember_me: true
        };
        // Log in with your account password
        that.form.validateFields(['username'.'password'.'inputCode'] and {force: true}, (err, values) => {
           if(! err) { loginParams.username = values.username loginParams.password = md5(values.password) loginParams.password = values.password// The Login method here is the Login() of the asynchronous actions in the calling user.js
             that.Login(loginParams).then((res) = > {
               that.loginSuccess()
             }).catch((err) = > {
               console.log('err',err) that.requestFailed(err); })})},}Copy the code

In terms of user login, it does not write the login logic in our login page Methods, but writes the login interface and the operations after login in the actions method. After the login page form is verified, vuex action is directly called to invoke the login interface. State of the user in the login interface.

In this way, the whole framework of vuex various parts of the application and the basic structure clear, if there is series is not up to the place where Suggestions combining my cover and file path screenshot check, if vuex had a certain application, but not for modular developer, I think some of his own thinking before you finish see to have some inspiration.

Of course, at the end of this article, it is not applicable to:

  • Developers who have not used VUex and do not know how to use state, mutation, getters, action, etc. (it is suggested to combine the official documents to see others’ interpretation, which is how I started)
  • Don’t laugh at me if you’ve got a Vuex strategy for your projects: “I’m not satisfied with it.

In addition, I write articles are generally to share their own learning exploration journey, may be more useful to more entry-level front-end or from the entry-level to advanced front-end developers, welcome to praise encouragement and comments: V :: V:

If the feedback is good, I will share a full journey of my blog using egg.js +Mysql+Vue next time. Since it is the first time for me to write a complete project using node framework, and it is the first time for me to use Mysql, I will attach a lot of problems encountered in using Egg and Mysql for the first time. Server deployment issues and code for beginners to get started.