One article teaches you to know the Vuex state machine

华为云开发者社区
中文
Abstract: Simply put, Vuex is a mechanism for realizing the global state (data) management of components, which can facilitate the sharing of data between components.

This article is shared from the HUAWEI Cloud Community " Vuex State Machine Quick Understanding and Application ", the original author: Northern Lights Night.

1. The concept of quick knowledge:

1. How to share data between components:

There are usually the following ways:

  1. The parent passes the value to the child: v-bind attribute binding;
  2. The child passes the value to the parent: v-on event binding;
  3. Sharing data between sibling components: EventBus;

2. What is vuex:

  1. According to official words, Vuex is a state management mode developed specifically for Vue.js applications. It uses centralized storage to manage the state of all components of the application, and uses corresponding rules to ensure that the state changes in a predictable manner. 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, state snapshot import and export, etc.
  2. Simply put, Vuex is a mechanism for realizing the global state (data) management of components, which can facilitate the sharing of data between components.

3. Advantages of using vuex:

  1. The shared data can be centrally managed in vuex, which is easy to develop and maintain.
  2. It can efficiently realize data sharing between components and improve development efficiency.
  3. The data stored in vuex is responsive and can keep the data and page synchronized in real time.
  4. Solve the message passing of non-parent and child components (store data in state).
  5. The number of AJAX requests is reduced, and some scenarios can be obtained directly from the state in memory.

In general, only data shared between components is necessary to be stored in vuex. As for the private data in the component, it is not necessary, it can still be stored in the data of the component itself. Of course, if you want all of them to be stored in vuex, it is also possible.

2. Basic usage:

1. Install dependent packages:

npm install vuex --save

2. Import dependent packages:

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

3. Create a store object:

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

const store = new Vuex.Store({
//state中存放的就是全局共享的数据
  state: {
    count: 0
  }
})

4. Mount the store object to the vue instance:

new Vue({
  el: '#app',
  store
})

At this point, all components can get data from the store.

3. Create a project:

The following is the vue project , there will be cases later:

(1) Open the cmd window and enter vue ui to open the visualization panel of vue:
image.png

(2) Select the new project path:
image.png

(3) Naming:
image.png

(4) Manually select the configuration, note that the vue2 version is used:
image.png
image.png

(5) Create:
image.png

(6) Next step:
image.png

(7) The creation is successful, go to the corresponding directory and open vscode to start programming:
image.png

(8) Running project:
image.png

Four. Explain the premise:

premise (note):

Write a small counter case, from the case with the concept to get started with vuex faster. So the code part in the core concept below is demonstrated based on this small case. Goal: Write two child components with a common count value. In the parent component, the count value decreases by 1 after one component is clicked, and the count value increases by 1 after one component is clicked.

The initial code of the parent component App.vue:

<template>
  <div id="app">
       <my-add></my-add>
       <p>--------------------</p>
       <my-reduce></my-reduce>
  </div>
</template>

<script>
// 引入组件
import Add from './components/Add.vue'
import Reduce from './components/Reduce.vue'
export default {
  name: 'App',
  data() {
    return {
      
    }
  },
  components: {
    'my-add': Add,
    'my-reduce': Reduce
  }

}
</script>

The initial code of the subcomponent Add.vue:

<template>
    <div>
        <p>count值为:</p>
           <button>+1</button>
    </div>
 
</template>
<script>
  export default{
      data() {
          return {
              
          }
      },
  }
</script>

The initial code of the subcomponent Reduce.vue:

<template>
    <div>
         <p>count值为:</p>
           <button>-1</button>
    </div>
</template>
<script>
  export default{
      data() {
          return {
              
          }
      },
  }
</script>

The initial code of the store object is:

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    count: 0
  }
})

Initial effect:
image.png

5. Core concepts:

1.state:

According to official words, it is as follows: Vuex uses a single state tree-yes, one object contains all application-level states. So far it has existed as a "single data source (SSOT)". This also means that each application will only contain one store instance.

Simply put, is the only public data source provided by State, and all shared data must be stored in the State of the Store.

1.1 The first way to access state in components:

Enter the following commands directly in the component:

As quoted in the Add.vue sub-component:

<template>
    <div>
        <p>count值为:{{this.$store.state.count}}</p>
           <button>+1</button>
    </div> 
</template>
//下面部分代码跟前面一样无改变,所以省略了

Looking at the effect, it shows that the value of count is 0:
image.png

1.2 The second way to access state in components:

(1) Import mapState function from vuex on demand

import { mapState } from 'vuex'
(2) Map the global data needed by the current component to the computed calculated properties of the current component through the mapState function just imported:

computed: {
   ...mapState([count])
}

Tips: Computed is used to monitor the variables defined by itself. The variables are not declared in data, but defined directly in computed, and then you can perform two-way data binding on the page to display the results or use it for other processing;

As quoted in the Reduce.vue sub-component:

<template>
    <div>
         <p>count值为:{{count}}</p>
           <button>-1</button>
    </div>
</template>
<script>
import {mapState} from 'vuex'
  export default{
      data() {
          return {
              
          }
      },
      computed: {
         ...mapState(['count'])
      }
  }
</script>

Looking at the effect, it also shows that the value of count is 0:
image.png

2. mutation:

According to official words, the only way to change the state in the Vuex store is to submit a mutation. Mutations in Vuex are very similar to events: each mutation has a string event type (type) and a callback function (handler). This callback function is where we actually make state changes, and it will accept state as the first parameter.

Simply put, Mutation is used to change the data in the Store.
① Store data can only be changed through mutation, and data in Store cannot be directly manipulated.
②Although the operation is a little cumbersome in this way, it can centrally monitor the changes of all data.

For example, to realize the operation of self-incrementing count value by 1, then define a function that self-increases by 1 in the first motivations. Then if you want to use the corresponding subcomponent, the component directly introduces the mutation and calls the corresponding function.

As follows, the Add.vue subcomponent needs to realize the self-increment 1 function: first define a function add that can realize self-increment in the mutations in the state machine:

export default new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    //自增加1函数
    add(state){
      state.count++
    }
  }
})

2.1 The first way to trigger mutation:

In the Add.vue subcomponent, bind the click event to the button and trigger the mutation:

<template>
    <div>
        <p>count值为:{{this.$store.state.count}}</p>
           <button @click="btnAdd">+1</button>
    </div>
 
</template>
<script>
  export default{
      data() {
          return {
              
          }
      },
      methods: {
          btnAdd() {
              // 第一种引入mutation的方式,触发add函数
              this.$store.commit('add')
          }
      }
  }
</script>

To see the effect, the click self-increase has been realized:
image.png

2.2 Trigger mutation and pass parameters:

Of course, when the function in the mutation is called in the component, parameters can also be passed. For example, there is an auto-increment function, but how much it increases depends on the parameters passed in when calling:

export default new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    // 传入参数,第一个一定是state,第二个为传入的参数
    //自增加 n 的函数
    addN(state,n){
      state.count+= n
    }
  }
})

Corresponding components need to pass in parameters when calling:

methods: {
          btnAdd2() {
              // 引入mutation的方式,触发addN函数
              // 并传参,自增加6吧
              this.$store.commit('addN',6)
          }
      }

2.3 The second way to trigger mutation:

(1) Import the mapMutations function from vuex on demand

import { mapMutations } from 'vuex'
(2) Map the required mutations function to the methods of the current component through the mapMutations function just imported:


methods: {
   ...mapMutations(['add','addN'])
}

Actually, realize the functional requirements of Reduce.vue component's click self-decrement 1:

State machine adds a decrement function:

export default new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    //自增加1函数
    add(state){
      state.count++
    },
    // 自减1的函数
    sub(state){
      state.count--
    }
  }
})

The Reduce.vue component clicks the button to achieve self-decrement 1:

<template>
    <div>
         <p>count值为:{{count}}</p>
           <button @click="btnSub">-1</button>
    </div>
</template>
<script>
//导入
import {mapState,mapMutations} from 'vuex'
  export default{
      data() {
          return {
              
          }
      },
      computed: {
         ...mapState(['count'])
      },
      methods: {
          // 映射mutation里的sub函数
          ...mapMutations(['sub']),
          // 要自减,调用sub函数
          btnSub(){
             this.sub()
          }
      }
  }
</script>

See the effect:
image.png

3.Action:

At this point, the case in the fourth major point has been completed, and self-increment and self-decrement have been realized. Now to improve the case, we need to click the button for one second and then self-increase and self-decrease. How can we achieve it? Can you add a 1-second timer to the function in the mutation in the state machine? This is definitely not working. because the mutation does not support asynchronous operation , then what to do, Dangdang, Action comes on stage.

Action can contain any asynchronous operation, so it is used to handle asynchronous tasks.

Action submits a mutation instead of directly changing the state. Remember that it cannot directly modify the data in the state, only the mutation can modify it. That is to say, if you change data through asynchronous operations, you must use Action instead of Mutation, but in Action, you must change the data indirectly by triggering Mutation.

First define the Action in the state machine:

export default new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    //自增加1函数
    add(state){
      state.count++
    },
    // 自减1的函数
    sub(state){
      state.count--
    }
  },
  // 定义action,里面的addAsync函数实现1秒后执行mutation里的add函数
   actions: {
    addAsync(context) {
      setTimeout(()=>{
      // 必须通过context.commit()触发mutation才行
         context.commit('add')
    },1000)
  }  
 }
})

The Action function accepts a context object with the same methods and properties as the store instance, so you can call context.commit to submit a mutation.

3.1 The first way to trigger Action:

Change the component Add.vue code, introduce Action, and realize asynchronous self-increment operation.

<template>
    <div>
        <p>count值为:{{this.$store.state.count}}</p>
           <button @click="btnAdd">+1</button>
    </div>
 
</template>
<script>
  export default{
      data() {
          return {
              
          }
      },
      methods: {
          btnAdd() {
              // 第一种引入Action的方式,触发addAsync函数
              // 这里的dispatch专门用来调用action函数
              this.$store.dispatch('addAsync')
          }
      }
  }
</script>

See the effect, realize the self-increasing after 1 second:
image.png

3.2 Trigger Action asynchronous task and pass parameters:

Of course, when the function in the action is called in the component, parameters can also be passed. For example, there is an auto-increment function that executes after clicking for 1 second, but how much it increases depends on the parameters passed in when calling:

definition:

export default new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
   // 传入参数,第一个一定是state,第二个为传入的参数
    //自增加 n 的函数
    addN(state,n){
      state.count+= n
    }
  },
   actions: {
    // 有参数 n,这个n又传给了mutation里的addN函数
    addNAsync(context,n) {
      setTimeout(()=>{
         context.commit('addN',n)
    },1000)
  }  
 }
})

Corresponding components need to pass in parameters when calling:

methods: {
          btnAdd2() {
              // 调用dispatch函数
              // 触发action时传参数,为 6 吧,表示自增6
              this.$store.dispatch('addNAsync',6)
          }
      }

3.3 The second way to trigger Action:

(1) Import mapActions function from vuex on demand

import { mapActions } from 'vuex'
(2) Map the required actions function to the methods of the current component through the mapActions function just imported:


methods: {
   ...mapActions(['add','addN'])
}

Actually, realize the functional requirement that the Reduce.vue component is clicked and reduced by 1 after one second:

Define subAsync in actions as a decrement function after one second:

export default new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    //自增加1函数
    add(state){
      state.count++
    },
    // 自减1的函数
    sub(state){
      state.count--
    }
  },
   actions: {
    addAsync(context) {
      setTimeout(()=>{
         context.commit('add')
    },1000)
  },
   subAsync(context) {
      setTimeout(()=>{
         context.commit('sub')
    },1000)
  }    
 }
})

Change the Reduce.vue code to realize the function:

<template>
    <div>
         <p>count值为:{{count}}</p>
           <button @click="btnSub">-1</button>
    </div>
</template>
<script>
//导入
import {mapState,mapActions} from 'vuex'
  export default{
      data() {
          return {
              
          }
      },
      computed: {
         ...mapState(['count'])
      },
      methods: {
          // 映射Action里的函数
          ...mapActions(['subAsync']),
          // 要自减,调用subAsync函数
          btnSub(){
             this.subAsync()
          }
      }
  }
</script>

See the effect:
image.png

4. Getter:

Getter is used to process the data in the Store to form new data. And it should be noted that it does not modify the data in the state.
① Getter can form new data after processing the existing data in the Store, similar to the calculated properties of Vue.
② If the data in the Store changes, the data of Getter will also change.

For example, there is a getter function that returns the current count+1:

4.1 The first way to trigger getters:

this.$store.getters.name
Shown in the App.vue component:

<template>
  <div id="app">
       <my-add></my-add>
       <p>--------------------</p>
       <my-reduce></my-reduce>
       <p>--------------------</p>
       <h3>{{this.$store.getters.showNum}}</h3>
  </div>
</template>

effect:
image.png

4.2 The second way to trigger getters:

(1) Import mapGetters function from vuex on demand

import { mapGetters } from 'vuex'
(2) Map the global data needed by the current component to the computed calculated properties of the current component through the mapGetters function just imported:

computed: {
   ...mapGetters(['showNum'])
}

Or use it in App.vue:

<template>
  <div id="app">
       <my-add></my-add>
       <p>--------------------</p>
       <my-reduce></my-reduce>
       <p>--------------------</p>
       <h3>{{showNum}}</h3>
  </div>
</template>

<script>
// 引入组件
import Add from './components/Add.vue'
import Reduce from './components/Reduce.vue'
// 导入 mapGetters函数
import {mapGetters} from 'vuex'
export default {
  name: 'App',
  data() {
    return {
      
    }
  },
  components: {
    'my-add': Add,
    'my-reduce': Reduce
  },
  // 引入 getter
  computed: {
    ...mapGetters(['showNum'])
  }

}
</script>

See, the same effect:
image.png

Click to follow, and get to know the fresh technology of

阅读 1.9k

开发者之家
华为云开发者社区,提供全面深入的云计算前景分析、丰富的技术干货、程序样例,分享华为云前沿资讯动态...

华为云开发者社区,提供全面深入的云计算前景分析、丰富的技术干货、程序样例,分享华为云前沿资讯动态...

1.3k 声望
1.7k 粉丝
0 条评论

华为云开发者社区,提供全面深入的云计算前景分析、丰富的技术干货、程序样例,分享华为云前沿资讯动态...

1.3k 声望
1.7k 粉丝
文章目录
宣传栏