When encapsulating the login logic of Hy-Vue-Admin, I initially used a very intuitive approach of using global cookies to store the login status. However, it felt awkward and cumbersome to write, so I decided to use Vuex, which is recommended by the official Vue, to manage the global state:
Vuex is a state management pattern designed specifically for Vue.js applications. It adopts a centralized storage to manage the state of all components in the application and ensures that the state changes in a predictable way. - Official definition
Reasons for Use#
- When developing single-page applications with Vue, it is often necessary to manipulate data or states shared between components:
- When the application is small, common methods of parent-child component communication such as props and events can be used, with one-way data flow.
- When the application is large and multiple components share the same state, the simplicity of one-way data flow can easily be compromised:
- Multiple views depend on the same state.
- Different views require changes to the same state.
- Problems with traditional solutions:
- For problem one: The method of passing parameters becomes very cumbersome and cannot handle the case of state transfer between sibling components in a nested component structure.
- For problem two: It is often used to directly reference parent-child components or use events to change and synchronize multiple copies of state between components. This pattern is very inefficient and can easily lead to unmaintainable code.
- New approach:
- Extract the shared state of components and manage it as a global singleton.
- Regardless of the position in the component tree, any component can directly access the state or trigger actions.
- By defining and isolating various concepts in state management and enforcing certain rules, the code becomes more structured and easier to maintain.
Let's start with an official diagram~~
Core Concepts#
State#
The concept of a single state tree, where each application contains only one store instance.
-
Vuex injects the state from the root component to every child component through the store option (
Vue.use(Vuex)
):const app = new Vue({ el: '#app', store, // provide the store object to the store option components: { Counter } })
-
Accessing Vuex state in Vue components: Child components access the store instance through
this.$store
const Counter = { template: `<div>{{ Count }}</div>`, computed: { count () { return this.$store.state.count } } }
-
Using
mapState
helper function and object spread operator. -
Components still have local state:
- Using Vuex does not necessarily mean that all states should be put into Vuex.
- If a state strictly belongs to a single component, it is best to keep it as the component's local state.
Mutation#
The only way to change the state in the Vuex store is by committing a mutation:
- Each mutation has a string event type and a callback function. The callback function is where we actually make the state changes, and it will receive the state as the first parameter by default.
const store = new Vuex.Store({
state: {
count: 1
},
mutations: {
increment (state) {
state.count++
}
}
})
- A mutation handler cannot be called directly. It should be registered as an event type: when a mutation of type
increment
is triggered, this function will be called.
store.commit('increment')
- Payload: Additional parameters can be passed to
store.commit
. - Mutations must be synchronous: Any state changes caused by a mutation event type should be completed at that moment.
Action#
Similar to mutations, but with the following differences:
- Actions commit mutations to change the state, rather than directly mutating the state.
- Actions can contain arbitrary asynchronous operations.
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment (context) {
context.commit('increment')
}
}
})
Action functions accept a context object with the same methods and properties as the store instance, so a mutation can be committed using context.commit
.
-
Actions are triggered using the
store.dispatch
method:store.dispatch('increment')
Module#
Using a single state tree can make the store object in the application become bloated. Therefore, Vuex allows us to divide the store into modules, each with its own state, mutations, actions, and even nested sub-modules.
Solution#
Write the global state management code in the src directory, which includes the user's state.
src
|—— api
|—— login.js # user login API interface
|—— ……
|—— ……
|—— store
|—— modules
|—— user.js # user module in the store
|—— getters.js
|—— index.js
|—— utils
|—— auth.js # operations related to user token
|—— request.js # interceptor for login requests using axios
User module in the store:
In Login.vue, when the login button is clicked, the Login action is dispatched:
In the actions of the user module, the login API is called first. After successfully receiving the token, the state token is set by committing a mutation and the token is saved using cookies:
This completes the overall logic of saving the login token state.
The logic for logging out is similar. When the logout button is clicked, an action is dispatched. After receiving a successful status code from the logout API, the state token is set to empty by committing a mutation and the cookie is deleted. You can read the implementation code yourself~