What is Vuex?
Vuex is a state management mode developed specifically for vue.js applications. It uses centralized storage to manage the state of all components of an application and rules to ensure that the state changes in a predictable way. Vuex is also integrated into Vue’s official debugging tool devTools Extension (Opens New Window), providing advanced debugging functions such as zero-configuration time-travel debugging and state snapshot import and export.
What is “state management mode”?
Let’s start with a simple Vue counting application:
new Vue({
// state
data () {
return {
count: 0
}
},
// view
template: `
<div>{{ count }}</div>
`,
// actions
methods: {
increment () {
this.count++
}
}
})
Copy the code
This state self-management application consists of the following parts:
- State, the data source that drives the application;
- View, to declaratively map state to the view;
- Actions, in response to changes in state caused by user input on the view.
Here is a simple illustration of the idea of “one-way data flow” :
However, the simplicity of one-way data flow can easily be broken when our application encounters multiple component shared state:
- Multiple views depend on the same state.
- Actions from different views need to change the same state.
For problem one, the method of passing parameters can be cumbersome for multi-layer nested components and does nothing to transfer state between sibling components. For problem two, we often use parent-child components to reference directly or to change and synchronize multiple copies of state through events. These patterns are very fragile and often result in unmaintainable code.
So why don’t we extract the shared state of the components and manage it in a global singleton? In this mode, our tree of components forms a giant “view” where any component can get state or trigger behavior no matter where it is in the tree!
By defining and isolating concepts in state management and by enforcing rules to maintain independence between views and states, our code becomes more structured and maintainable.
This is The basic idea behind Vuex, which borrows from Flux (a Labour of New Window), Redux (a Labour of New Window) and The Elm Architecture (a Labour of New Window). Unlike other patterns, Vuex is a state management library designed specifically for vue.js to leverage the fine-grained data response mechanism of vue.js for efficient state updates.
If you want to learn Vuex interactively, Scrimba’s Vuex course, a hybrid of screen recording and code proving ground, opens New Window and allows you to pause and try.
#When should I use Vuex?
Vuex helps us manage shared state and comes with more concepts and frameworks. This requires a trade-off between short-term and long-term benefits.
Using Vuex can be tedious and redundant if you don’t plan to develop large, single-page applications. That’s true — if your application is simple, you’d better not use Vuex. A simple Store pattern (opens New Window) is all you need. However, if you need to build a medium to large single-page application, and you’re probably thinking about how to better manage state outside of components, Vuex would be a natural choice. To quote Redux author Dan Abramov:
Flux architectures are like glasses: you know when you need them.
start
Try this lesson on Scrimba
At the heart of every Vuex application is the Store. A “store” is basically a container that contains most of the states in your app. Vuex differs from a purely global object in two ways:
- Vuex’s state storage is reactive. When the Vue component reads the state from the Store, if the state in the store changes, the corresponding component is updated efficiently accordingly.
- You can’t just change the state in the store. The only way to change the state in a store is to commit mutation explicitly. This allows us to easily track each state change, which allows us to implement tools that help us better understand our application.
#The simplest Store
prompt
We will use ES2015 syntax in the document sample code that follows. If you haven’t mastered ES2015 yet, it’s a Labour of love!
With Vuex installed, let’s create a Store. The creation process is straightforward — just provide an initial state object and some mutation:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
}
})
Copy the code
Now you can retrieve state objects via store.state and trigger state changes via the store.mit method:
store.commit('increment')
console.log(store.state.count) // -> 1
Copy the code
To access this.$Store property in a Vue component, you need to provide a created store for the Vue instance. Vuex provides a mechanism to “inject” the store from the root component to all child components in the form of store options:
new Vue({
el: '#app',
store: store,
})
Copy the code
prompt
If using ES6, you can also use the property abbreviation for an ES6 object (used when the key of an object’s property has the same name as the variable passed in) :
new Vue({
el: '#app',
store
})
Copy the code
Now we can commit a change from the component’s method:
methods: {
increment() {
this.$store.commit('increment')
console.log(this.$store.state.count)
}
}
Copy the code
Again, we committed mutation rather than changing store.state.count directly because we wanted to track state changes more explicitly. This simple convention makes your intentions clear, making it easier to interpret in-app state changes as you read the code. In addition, this gives us the opportunity to implement debugging tools that record every state change and save a snapshot of the state. With it, we can even implement a time-travel debugging experience.
Because the state in a store is reactive, calling the state in a store in a component is as simple as simply returning it in a evaluated property. Triggering changes is also just a mutation submission in the component’s methods.
This is a basic Vuex numeration application (opens New Window) example.
Vuex core concepts
Rewrite the code
Money. Use Vuex vue
/store/index.ts import Vue from 'vue'; import Vuex from 'vuex'; import clone from '@/lib/clone'; Vue.use(Vuex); Results = new vuex. store ({state: {recordList: [] as RecordItem[]}, mutations: { fetchRecords(state) { state.recordList = JSON.parse(window.localStorage.getItem('recordList') || '[]') as RecordItem[]; }, createRecord(state, record) { const record2: RecordItem = clone(record); record2.createdAt = new Date(); this.recordList && state.recordList.push(record2); store.commit('saveRecords'); }, saveRecords(state) { window.localStorage.setItem('recordList', JSON.stringify(state.recordList)); }}}); export default store;Copy the code
created() { this.$store.commit('fetchRecords'); } saveRecord() {this. code. store.mit ('createRecord', this.record); }Copy the code
Labels. Use Vuex vue
created() { this.$store.commit('fetchTags'); } createTag() {const name = window.prompt(' Please enter tag name '); if (! Name) {return window.alert(' label name cannot be empty '); } this.$store.commit('createTag', name); }}Copy the code
Use Vuex EditLabel. Vue
<template> <Layout> <div class="navBar"> <Icon class="leftIcon" name="return" @click="goBack"/> <span Class ="title"> <span >< span class="rightIcon"></span> </div> <div class="form-wrapper"> <FormItem :value="tag.name" @update:value="update" field-name=" placeholder "placeholder=" please input tag name "/> </div> <div class="button-wrapper"> < button @click="remove"> delete tag </Button> </div> </Layout> </template> <script lang="ts"> import Vue from 'Vue '; import {Component} from 'vue-property-decorator'; import FormItem from '@/components/Money/FormItem.vue'; import Button from '@/components/Button.vue'; @Component({ components: {Button, FormItem}, }) export default class EditLabel extends Vue { get currentTag() { return this.$store.state.currentTag; } created() { const id = this.$route.params.id; this.$store.commit('fetchTags'); this.$store.commit('setCurrentTag', id); if (! this.currentTag) { this.$router.replace('/404'); } } update(name: string) { if (this.currentTag) { this.$store.commit('updateTag', { id: this.currentTag.id, name }); } } remove() { if (this.currentTag) { this.$store.commit('removeTag', this.currentTag.id); } } goBack() { this.$router.back(); } } </script>Copy the code
Problem: You should use GET
The previous function didn’t find a problem because it didn’t use elements in created, for example
created() {
const id = this.$route.params.id;
this.$store.commit('fetchTags');
this.$store.commit('setCurrentTag', id);
if (!this.currentTag) {
this.$router.replace('/404');
}
}
Copy the code
CurrentTag is used here, but if you put currentTag in computed, created will not be available, so you should put the data in GET, as shown in figure 2
get currentTag() {
return this.$store.state.currentTag;
}
Copy the code
So modify computed to TAG from the previous builds
The final index. Ts
UpdateTag (state, payload: {id: string, name: string})
import Vue from 'vue'; import Vuex from 'vuex'; import clone from '@/lib/clone'; import createId from '@/lib/createId'; import router from '@/router'; Vue.use(Vuex); type RootState = { recordList: RecordItem[], tagList: Tag[], currentTag? : Tag } const store = new Vuex.Store({ state: { recordList: [], tagList: [], currentTag: undefined } as RootState, mutations: { setCurrentTag(state, id: string) { state.currentTag = state.tagList.filter(t => t.id === id)[0]; }, updateTag(state, payload: { id: string, name: string }) { const {id, name} = payload; const idList = state.tagList.map(item => item.id); if (idList.indexOf(id) >= 0) { const names = state.tagList.map(item => item.name); If (names.indexof (name) >= 0) {window.alert(' label name repeated '); } else { const tag = state.tagList.filter(item => item.id === id)[0]; tag.name = name; store.commit('saveTags'); } } }, removeTag(state, id: string) { let index = -1; for (let i = 0; i < state.tagList.length; i++) { if (state.tagList[i].id === id) { index = i; break; } } if (index >= 0) { state.tagList.splice(index, 1); store.commit('saveTags'); router.back(); } else {window.alert(' delete failed '); }}, fetchRecords(state) { state.recordList = JSON.parse(window.localStorage.getItem('recordList') || '[]') as RecordItem[]; }, createRecord(state, record) { const record2: RecordItem = clone(record); record2.createdAt = new Date(); state.recordList.push(record2); store.commit('saveRecords'); }, saveRecords(state) { window.localStorage.setItem('recordList', JSON.stringify(state.recordList)); }, fetchTags(state) { state.tagList = JSON.parse(window.localStorage.getItem('tagList') || '[]'); }, createTag(state, name: string) { const names = state.tagList.map(item => item.name); If (names.indexof (name) >= 0) {window.alert(' label name repeated '); } const id = createId().toString(); state.tagList.push({id, name: name}); store.commit('saveTags'); Window.alert (' added successfully '); }, saveTags(state) { window.localStorage.setItem('tagList', JSON.stringify(state.tagList)); }}}); export default store;Copy the code