[Vue] Vuex of Family Bucket

z@[toc]

overview

Vuex is a state management pattern + library developed specifically for Vue.js applications. It uses a 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.

How to share data between components

  • Passing value from parent to child: v-bind attribute binding
  • Passing value from child to parent: v-on event binding
  • Sharing data between sibling components: EventBus
  • $on component that receives data
  • $emit component that sends data

imagine a scene

If there are many pages (components/views) in your project, and there are multi-level nesting relationships between pages, at this time, if these pages all need to share a state, the following two problems will arise at this time:

  • Multiple views depend on the same state
  • Actions from different views need to mutate the same state

Move your little head and you will think of solutions to the above methods:

  • For the first problem, if it is a multi-level nested relationship, you can use the parent-child component to pass parameters to solve it. Although it is a bit troublesome, it can be solved; it is difficult for sibling components or components with more complex relationships , although it can be solved by various methods, it is really not elegant, and when the project grows bigger, the code will become a mountain of shit, which is really annoying.
  • For the second problem, you can directly reference through parent-child components, or change or synchronize multiple copies of the state through events. This mode is very fragile and often makes the code difficult to maintain, and it also turns the code into a mountain of shit.

At this point, now that I have thought about this, what if I change my mind:

  • Extract the same state that each component needs to depend on, and use the singleton mode to manage it globally.
  • In this mode, any component can directly access this state, or when the state changes, all components get updated.

At this time, Vuex was born!

This is the basic idea behind Vuex, borrowed from Flux and Redux. Different from other modes, Vuex is a state management library specially designed for Vue to use Vue.js' fine-grained data response mechanism for efficient state updates.

The vuex usage cycle diagram of the official website
insert image description here

Install

npm install vuex --save

use

import

import Vuex from 'vuex'
Vue.use(Vuex)

create store object

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

Mount the store object

new Vue({
    
     
    el: '#app', 
    render: h=>h(app)m 
    router, 
    //将创建的共享数据对象,挂载到Vue实例中
    //所有的组件,就可以直接从store中获取全局的数据了
    store 
})

Create a vue project with vuex

  1. Open the terminal and enter the command: vue ui
  2. When the project dashboard is opened, we click the project management drop-down list in the upper left corner of the page, and then click Vue project manager
  3. Click to create project
  4. Set project name and package manager
  5. Set up manual configuration items
  6. Set function items
  7. create project

Project code formatting:

Create a .prettierrc file in the project root directory (same level as src), and write the code as follows:

{
    
    
    "semi":false,
    "singleQuote":true
}

Core idea

State

The concept
State is to provide the only public data source, and all shared data must be stored in the State of `Store.

If state information is stored in multiple Store objects, subsequent management and maintenance will become particularly difficult, so Vuex also uses a single state tree (Single Source of Truth) to manage all states at the application level .

A single state tree allows us to find a fragment of a certain state in the most direct way, and it can also be managed and maintained very conveniently in the subsequent maintenance and debugging process.

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

State data access method 1

Access through this.$store.state.global data name, eg.

<h3>当前最新Count值为:{
   
   {this.$store.state.count}}</h3>

State data access method 2

Import mapState function on demand from vuex

import {
    
     mapState } from 'vuex'

Through the mapState function just imported, the global data required by the current component is mapped to the computed property of the current component:

<template>
  <div>
    <h3>当前最新Count值为:{
    
    {
    
     count }}</h3>
    <button>-1</button>
  </div>
</template>
<script>
import {
    
     mapState } from "vuex";
export default {
    
    
  computed: {
    
    
    ...mapState(["count"])
  }
};
</script>

Getter

  • Getters are used to process the data in the Store to form new data, similar to the computed properties in Vue
  • If the data in the Store changes, the data in the Getters will also change accordingly.

Use one

Define getters in index.js

//定义 Getter
const store = new Vuex.Store({
    
    
    state:{
    
    
    count: 0
    },
    getters:{
    
    
        showNum(state){
    
    
          return '当前Count值为:['+state.count']'
        }
      }
})

In the component use:

this.$store.getters.名称
<h3>{
    
    {
    
     this.$store.getters.showNum }}</h3>

Use method two

In the component, import the mapGetters function

import {
    
     mapGetters } from 'vuex'

Through the mapGetters function imported just now, map the required getters function to the computed method of the current component:

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

When using it, just call it directly:

<h3>{
    
    {
    
     showNum }}</h3>

Mutation

introduce

If you want to modify the value of count, what should you do?

Maybe you are smart, you have already thought that you can directly operate this.$store.state.count in the component, the code is as follows

<template>
  <div>
    <h3>当前最新Count值为:{
    
    {
    
    this.$store.state.count}}</h3>
    <button @click="add">+1</button>
  </div>
</template>
<script>
export default {
    
    
  methods: {
    
    
    add() {
    
    
      this.$store.state.count++;
    }
  }
};
</script>

The test found that this can meet the requirements and complete the +1 operation.

However, this method is strictly prohibited in vuex, so how to do it? At this time, you need to use Mutation.
concept

Mutation is used to change the data stored in the Store.

  • The Store data can only be changed through mutation, and the data in the Store cannot be directly manipulated
  • In this way, although the operation is a bit cumbersome, all data changes can be monitored centrally, and direct operation of Store data cannot be monitored.

Define the Mutation function

Define functions in mutations, as follows:

mutations: {
    
        
      // 自增    
      add(state) {
    
          
      state.count++    
      }  
  }

The defined function will have a default parameter state, which is the state object stored in the Store.
Call the Mutation function

Asynchronous operations cannot be performed in Mutation. If asynchronous operations are required, please handle them in Action

Method 1:
In the component, complete the trigger through this.$store.commit (method name)

mutations: {
    
        
      // 自增    
      add(state) {
    
          
      state.count++    
      }  
  }

Method 2:
Import the mapMutations function in the component

import {
    
     mapMutations } from 'vuex'

Through the mapMutations function imported just now, map the required mutations function to the methods method of the current component:

methods:{
    
    
    ...mapMutations('add','addN'),
    // 当前组件设置的click方法
    addCount(){
    
    
        this.add()
    }
}

Mutation passing parameters

When updating data through mutation, it is sometimes necessary to carry some additional parameters. Here, the parameters are called the payload of the mutation.

If there is only one parameter, the payload corresponds to this parameter value. For example,
insert image description here
if there are multiple parameters, it will be passed in the form of an object. At this time, the payload is an object, and relevant data can be retrieved from the object.

When defining functions in mutations, parameters can also be received, examples are as follows:

mutations: {
    
    
    // 自增
    add(state) {
    
    
      state.count++
    },
    // 带参数
    addNum(state, payload) {
    
    
      state.count += payload.number
    }
  }

In the component, call it like this:

methods: {
    
    
  add() {
    
    
    //   this.$store.state.count++;
    this.$store.commit("add");
  },
  addNum() {
    
    
    this.$store.commit("addNum", {
    
    
      number: 10
    });
  }
}

Mutation response rules

The State in the Vuex store is responsive. When the data in the State changes, the Vue components will also be automatically updated.

This requires us to abide by some rules corresponding to Vuex:

  • Initialize the required properties in the store in advance
  • When adding new properties to objects in State, use the following:
    • Use Vue.set(obj, 'newProp', 'propValue')
    • Reassign old object with new object
      Sample code:
updateUserInfo(state) {
    
    
      // 方式一
      Vue.set('user', 'address', '北京市')
      // 方式二
      state.user = {
    
    
        ...state.user,
        'address': '上海市'
      }
    }

Mutation constant type

introduce

Think about a question:

In mutation, we define many event types (that is, the method names). As the project grows larger, Vuex manages more and more states, and there are more and more situations that need to update the state, which means There are more and more methods in Mutation.

When there are too many methods, users need to spend a lot of time and energy to remember these methods, and even switch back and forth between multiple files to check the method names, and there are also cases of copying or spelling errors.

So how to avoid it?

  • In various Flux implementations, a very common solution is to use constants instead of Mutation event types
  • These constants can be placed in a separate file for easy management, and all event types of the entire App can be seen at a glance

solution

  • Create a mutation-types.js file where you define constants
  • When defining constants, you can use the style in ES2015, using a constant as the name of the function
  • Just import the file at the use

New mutation-types.js:
insert image description here
introduced and used in store/index.js:

import Vue from 'vue'
import Vuex from 'vuex'
import * as types from './mutation-type'

Vue.use(Vuex)

export default new Vuex.Store({
    
    
  state: {
    
    
    count: 0,
    user: {
    
    
      name: '旺财',
      age: 12
    }
  },
  mutations: {
    
    
    // 自增
    [types.ADD_NUM](state) {
    
    
      state.count++
    },
}

In the component, import and call:

<script>
import {
    
     ADD_NUM } from "../store/mutation-type";
export default {
    
    
  methods: {
    
    
    add() {
    
    
      this.$store.commit(ADD_NUM);
      //   this.addAsync();
      //   this.$store.state.count++;
      //   this.$store.commit("add");
    }
  }
};
</script>

Action

Action is similar to Mutation, but it is used to handle asynchronous tasks, such as network requests, etc.

If you change data through asynchronous operations, you must use Action instead of Mutation, but you still need to indirectly change data by triggering Mutation in Action.

parameter context

The methods defined in actions will have a default value context.

  • context is an object with the same methods and properties as the store object
  • Commit-related operations can be performed through context, and context.state data can be obtained

But they are not the same object, the difference will be introduced in Modules.

Use one

In index.js, add actions and corresponding methods:

export default new Vuex.Store({
    
    
  state: {
    
    
    count: 0
  },
 //只有 mutations 中定义的函数,才有权力修改 state 中的数据
  mutations: {
    
    
    // 自增
    add(state) {
    
    
      state.count++
    }
  },
  actions: {
    
    
    addAsync(context) {
    
    
      setTimeout(() => {
    
    
      //在 action 中,不能直接修改 state 中的数据
      //必须通过 context.commit() 触发某个 mutation 才行
        context.commit('add')
      }, 1000);
    }
  }
})

Called in the component:

<script>
export default {
    
    
  methods: {
    
    
    addNumSync(){
    
    
        // dispatch函数 专门用于触发 Action
        this.$store.dispatch('addAsync')
    }
  }
};
</script>

Use method two

In the component, import the mapActions function

import {
    
     mapActions } from 'vuex'

Through the mapActions function imported just now, map the required actions function to the methods method of the current component:

<script>
import {
    
     mapActions } from "vuex";
export default {
    
    
  methods: {
    
    
    ...mapActions(["addAsync"]),
    add() {
    
    Î
        this.addAsync()
    },
}

Use method three

After importing mapActions, you can directly bind the specified method to the @click event.

...mapActions(["addAsync"]),
---------------------------
 <button @click="addAsync">+1(异步)</button>

This method also applies to imported mapMutations

Actions carry parameters

In the actions of index.js, add the method of carrying parameters, as follows:

export default new Vuex.Store({
    
    
  state: {
    
    
    count: 0
  },
  mutations: {
    
    
    // 带参数
    addNum(state, payload) {
    
    
      state.count += payload.number
    }
  },
  actions: {
    
    
    addAsyncParams(context, payload) {
    
    
      setTimeout(() => {
    
    
        context.commit('addNum', payload)
      }, 1000);
    }
  }
})

In the component, call it like this:

methods: {
    
    
    addNumSyncParams() {
    
    
      this.$store.dispatch("addAsyncParams", {
    
    
        number: 100
      });
    }
  }

Combining Actions with Promises

Promise is often used for asynchronous operations. In Action, you can put asynchronous operations in Promise, and call the corresponding resolve or reject after success or failure.

Example:

In store/index.js, add an asynchronous method for actions:

actions: {
    
    
    loadUserInfo(context){
    
    
      return new Promise((resolve)=>{
    
    
        setTimeout(() => {
    
    
          context.commit('add')
          resolve()
        }, 2000);
      })
    }
  }

Called in the component, as follows:

methods: {
    
    
    addPromise() {
    
    
      this.$store.dispatch("loadUserInfo").then(res => {
    
    
        console.log("done");
      });
    }
}

Module

concept

Module means module, why use module in Vuex?

  • Vues uses a single state tree, which means that many states will be managed by Vuex
  • When the application becomes very complex, the Store object may become quite bloated
  • To solve this problem, Vuex allows us to divide the store into modules (Module), and each module has its own State, Mutation, Actions, Getters, etc.

use

In the store directory, create a new folder modules to store the modules files of each module, here moduleA is taken as an example.

In the modules folder, create a new moduleA.js, and the internal properties, state, mutations, etc. are the same as before. Please refer to the code for comments. The example is as follows:

export default {
    
    
    state: {
    
    
        name: '凤凰于飞'
    },
    actions: {
    
    
        aUpdateName(context) {
    
    
            setTimeout(() => {
    
    
                context.commit('updateName', '旺财')
            }, 1000);
        }
    },
    mutations: {
    
    
        updateName(state, payload) {
    
    
            state.name = payload
        }
    },
    getters: {
    
    
        fullName(state) {
    
    
            return state.name + '王昭君'
        },
        fullName2(state, getters) {
    
    
            // 通过getters调用本组方法
            return getters.fullName + ' 礼拜'
        },
        fullName3(state, getters, rootState) {
    
    
            // state代表当前module数据状态,rootState代表根节点数据状态
            return getters.fullName2 + rootState.counter
        }
    }
}
  • The local state is exposed through context.state, and the root node state is context.rootState

Reference moduleA in store/index.js as follows:

import Vue from "vue"
import Vuex from "vuex"

import moduleA from './modules/moduleA'

Vue.use(Vuex)

const store = new Vuex.Store({
    
    
    modules: {
    
    
        a: moduleA
    }
})

export default store

In this way, the modular splitting of state management is completed through sub-modules.

optimization

If the project is very complex, in addition to sub-module division, the actions, mutations, getters, etc. of the main module can also be separated out, split into separate js files, exported through export, and then imported into index.js for use.

Example: separate the actions, mutations, and getters of the main module into js files and export them, taking actions.js as an example,

export default{
    
    
    aUpdateInfo(context, payload) {
    
    
        return new Promise((resolve, reject) => {
    
    
            setTimeout(() => {
    
    
                context.commit('updateInfo')
                resolve()
            }, 1000);
        })
    }
}

In store/index.js, import and use, as follows:

import Vue from "vue"
import Vuex from "vuex"
import mutations from './mutations'
import actions from './actions'
import getters from './getters'
import moduleA from './modules/moduleA'


Vue.use(Vuex)

const state = {
    
    
    counter: 1000,
    students: [
        {
    
     id: 1, name: '旺财', age: 12 },
        {
    
     id: 2, name: '小强', age: 31 },
        {
    
     id: 3, name: '大明', age: 45 },
        {
    
     id: 4, name: '狗蛋', age: 78 }
    ],
    info: {
    
    
        name: 'keko'
    }
}

const store = new Vuex.Store({
    
    
    state,
    mutations,
    getters,
    actions,
    modules: {
    
    
        a: moduleA
    }
})

export default store

Final project directory diagram:
insert image description here

source

Teach you to use Vuex step by step, a tutorial Vue series that monkeys can understand - Vuex Detailed Explanation

Guess you like

Origin blog.csdn.net/weixin_44231544/article/details/132333567