1, the preface


Recently, I reviewed the vUE family bucket, and it was more impressive to watch it again, so I specially recorded it (vuex version of this article is V3.x).

2. What is Vuex


Vuex is a state management mode developed specifically for vue.js. It uses centralized storage to manage the state of all components and rules to ensure that the state changes in a predictable way (global variables, as I understand them).

3. Description of 5 major attributes


state

Object type, similar to the instance’s data property, that holds data

getters

Object type, similar to instance computed property computed

mutations

Object type, similar to instance methods, but unable to handle asynchronous methods

actions

Object types, similar to instance methods, can handle asynchronous methods

modules

Object type. When there is a lot of state content, it is divided into small modules by using this attribute. Each module has its own state, mutation, action and getter

4, the state


Data stored in state follows the same rules as data in Vue instances and must be pure objects.

4.1 Direct Access

this.$store.state.xxx
Copy the code

4.1 Using mapState Mapping

<template>
	<div id="communication">
		<p>Count: {{getCount}}</p>
		<p>School: {{getSchool(' I am a parameter ')}}</p>
	</div>
</template>

<script>
import { mapState } from 'vuex'

export default {
	name: 'Vuex'.data() {
		return {
			date: 1998}},computed: {
		...mapState({
			// mapState passes state as the first argument by default
			getCount: state= > state.count,
			getSchool(state) {
				return (val) = > {
					return state.school + val + this.date
				}
			}
		})
	},
	mounted() {
		// Take the value directly
		console.log(this.$store.state.count)
	}
}
</script>
Copy the code

5, getters


The return value of the getter is cached based on its dependency and recalculated only if its dependency value changes, and it accepts state as its first argument by default, or other getters as its second argument (as in the following example)

5.1 First define getters in VUEX

export default new Vuex.Store({
	state: {
		count: 0.school: 'Tsinghua University'
	},
	getters: {
		// Return the state value after processing
		getValue(state) {
			return state.count + '! '
		},
		// Return the state value after calling its own getters processing
		getGetters(state, getters) {
			return state.school + getters.getValue
		},
		// The value that is processed after receiving an external argument (when accessed through a method, the call is made each time, without caching the result)
		getParam(state) {
			return (param) = > {
				return state.school + param
			}
		}
	},
	mutations: {},
	actions: {},
	modules: {}})Copy the code

5.2 Obtaining the Value Directly

/ / value
console.log(this.$store.getters.getGetters)
// Pass the parameter value
console.log(this.$store.getters.getParam('param'))
Copy the code

5.3 Mapping using mapGetters

<template>
	<div id="communication">
		<p>Count: {{getGetters}}</p>
		<p>School: {{getParam(date)}}</p>
	</div>
</template>

<script>
import { mapGetters } from 'vuex'

export default {
	name: 'Vuex'.data() {
		return {
			date: 1998}},computed: {
		...mapGetters([
			'getGetters'.'getParam'])},mounted() {
		// Take the value directly
		console.log(this.$store.getters.getGetters)
		console.log(this.getParam(this.date))
	}
}
</script>
Copy the code

6, Mutation


Change the value in store by calling this. codestore.mit (‘ XXX ‘), calling the methods in mutation

6.1. Register the event on mutations first

export default new Vuex.Store({
	state: {
		count: 0.school: 'Tsinghua University'
	},
	getters: {},
	mutations: {
		// Default state as the first argument
		handleAdd(state) {
			state.count++
		},
		// Accept input
		handleChange(state, value) {
			state.school = value
		}
	},
	actions: {},
	modules: {}})Copy the code

6.2. Call the commit method in the component to modify the value

<template>
	<div id="communication">
		<p>Count: {{count}}</p>
		<el-button @click="handleStoreAdd">increase</el-button>
		<el-button @click="handleStoreChange">The ginseng</el-button>
	</div>
</template>

<script>
import { mapState } from 'vuex'

export default {
	name: 'Vuex'.data() {
		return {
			school: 'Wuhan University'}},computed: {
		...mapState([
			'count'])},methods: {
		// Call the modification
		handleStoreAdd() {
			this.$store.commit('handleAdd')},// Pass the parameter modification
		handleStoreChange() {
			this.$store.commit('handleChange'.this.school)
		}
	}
}
</script>
Copy the code

6.3. Use constants to define method names

Create a new file, mutation-types.js, define constants for method names, and export them

export const ADD_COUNT = 'ADD_COUNT'
export const CHANGE = 'CHANGE'
Copy the code

In the store

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

Vue.use(Vuex)

export default new Vuex.Store({
	state: {
		count: 0.school: 'Tsinghua University'
	},
	getters: {},
	mutations: {
		// Default state as the first argument
		[MT.ADD_COUNT](state) {
			state.count++
		},
		// Accept input
		[MT.CHANGE](state, value) {
			state.school = value
		}
	},
	actions: {},
	modules: {}})Copy the code

In the component

<template>
	<div id="communication">
		<p>Count: {{count}}</p>
		<el-button @click="handleStoreAdd">increase</el-button>
		<el-button @click="handleStoreChange">The ginseng</el-button>
	</div>
</template>

<script>
import { mapState } from 'vuex'
import * as MT from '.. /.. /store/mutation-types'
export default {
	name: 'Vuex'.data() {
		return {
			school: 'Wuhan University'}},computed: {
		...mapState([
			'count'])},methods: {
		// Call the modification
		handleStoreAdd() {
			this.$store.commit(MT.ADD_COUNT)
		},
		// Pass the parameter modification
		handleStoreChange() {
			this.$store.commit(MT.CHANGE, this.school)
		}
	}
}
</script>
Copy the code

6.4. Use mapMutations mapping

<template>
	<div id="communication">
		<p>Count: {{count}}</p>
		<p>Count: {{school}}</p>
		<el-button @click="handleStoreAdd">increase</el-button>
		<el-button @click="handleStoreChange(schools)">The ginseng</el-button>
	</div>
</template>

<script>
import { mapState, mapMutations } from 'vuex'
import * as MT from '.. /.. /store/mutation-types'

export default {
	name: 'Vuex'.data() {
		return {
			schools: 'Wuhan University'}},computed: {
		...mapState([
			'count'.'school'])},methods: {
		...mapMutations({
			handleStoreAdd: MT.ADD_COUNT,
			handleStoreChange: MT.CHANGE
		})
	}
}
</script>
Copy the code

7, the Action


Note that actions commit mutation, not direct change state, and can contain arbitrary asynchronous operations

7.1, defined in store

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

Vue.use(Vuex)

export default new Vuex.Store({
	state: {
		count: 0.school: 'Tsinghua University'
	},
	getters: {},
	mutations: {
		// Default state as the first argument
		[MT.ADD_COUNT](state) {
			state.count++
		},
		// Accept input
		[MT.CHANGE](state, value) {
			state.school = value
		}
	},
	actions: {
		add(context) {
			context.commit(MT.ADD_COUNT)
		}
	},
	modules: {}})Copy the code

7.2, used in components

<template>
	<div id="communication">
		<p>Count: {{count}}</p>
		<el-button @click="actionAdd">increase</el-button>
	</div>
</template>

<script>
import { mapState, mapMutations } from 'vuex'
import * as MT from '.. /.. /store/mutation-types'

export default {
	name: 'Vuex'.data() {
		return {
			schools: 'Wuhan University'}},computed: {
		...mapState([
			'count'.'school'])},methods: {
		...mapMutations({
			handleStoreAdd: MT.ADD_COUNT,
			handleStoreChange: MT.CHANGE
		}),
		// Call the action method with $store.dispatch
		actionAdd() {
			this.$store.dispatch('add')}}}</script>
Copy the code

7.3. Use mapActions mapping

import { mapActions } from 'vuex'

methods: {
	...mapActions([
		'moduleFn'])}Copy the code

or

import { mapActions } from 'vuex'

methods: {
	...mapActions([
		fn: 'moduleFn'])}Copy the code

7.4. Simplified writing

The Action accepts a context parameter object with the same methods and properties as the store instance, so you can submit a mutation by calling context.mit. Or get state and getters from context.state and context.getters, which can be simplified using ES6 deconstruction.

actions: {
  add({ commit, state }) {
    commit(MT.CHANGE, state.school)
  }
}
Copy the code

7.5. Perform asynchronous operations

In vuex

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

Vue.use(Vuex)

export default new Vuex.Store({
	state: {
		count: 0
	},
	getters: {},
	mutations: {
		// Default state as the first argument
		[MT.ADD_COUNT](state) {
			state.count++
		}
	},
	actions: {
		add({ commit }) {
			return new Promise((resolve, reject) = > {
				setTimeout(() = > {
					commit(MT.ADD_COUNT)
					resolve()
				}, 1000)})}},modules: {}})Copy the code

Use async/await or then/catch to handle async in components

<template>
	<div id="communication">
		<p>Count: {{count}}</p>
		<el-button @click="actionAdd">increase</el-button>
	</div>
</template>

<script>
import { mapState, mapMutations } from 'vuex'
import * as MT from '.. /.. /store/mutation-types'

export default {
	name: 'Vuex'.data() {
		return {
			schools: 'Wuhan University'}},computed: {
		...mapState([
			'count'.'school'])},methods: {
		...mapMutations({
			handleStoreAdd: MT.ADD_COUNT,
			handleStoreChange: MT.CHANGE
		}),
		// Call the action method with $store.dispatch
		async actionAdd() {
			await this.$store.dispatch('add')
			console.log(1998)}}}</script>
Copy the code

Eight Modules


When the application becomes very complex, the Store object can become quite bloated. At this point, the Store can be split into modules, each with its own state, mutation, action, getter, and even nested submodules, split from top to bottom in the same way.

8.1. Preparation

Create modulesa. js, modulESB.js in the Modules folder under the Store directory, as shown below

Write state, mutation, action, and getter for the local module in modulesa.js and export it

const moduleA = {
	state: () = > ({
		a: 'I am moduleA'
	}),
	getters: {},
	mutations: {},
	actions: {}}export default moduleA
Copy the code

It’s then imported into the store index.js and thrown into the Modules object

import Vue from 'vue'
import Vuex from 'vuex'
import * as MT from './mutation-types'
import moduleA from './modules/moduleA'
import moduleB from './modules/moduleB'

Vue.use(Vuex)

export default new Vuex.Store({
	state: {
		count: 0
	},
	getters: {},
	mutations: {},
	actions: {},
	modules: {
		moduleA,
		moduleB
	}
})
Copy the code

8.2, use the module state injected in modules

Used directly in components

this.$store.state.moduleA.xxx
Copy the code

Use mapState mapping in components

<span>{{ moduleA.xxx }}</span>

import { mapState } from 'vuex'

computed: {
	...mapState([
		'moduleA'])}Copy the code

8.3, use modules to inject module getters

Used directly in components

this.$store.getters.getModuleA
Copy the code

Use mapState mapping in components

<p>{{ getModuleA }}</p>

import { mapGetters } from 'vuex'

computed: {
	...mapGetters([
		'getModuleA'])}Copy the code

The getters inside the module, and the parameters state and getters it accepts, are the module’s local state objects, while the state of the root node is exposed as the third parameter rootState

const moduleA = {
	getters: {
		getModuleA(state, getters, rootState) {
			return state.xxx + The '-' + rootState.xxx
		}
	}
}
Copy the code

If you need to take parameters

const moduleA = {
	getters: {
		getModuleA(state, getters, rootState) {
			return (value) = > {
				return state.a + The '-' + value
			}
		}
	}
}
Copy the code

8.4, use mutations in Modules to inject the module

Used directly in components

this.$store.commit('setModuleA') | |this.$store.commit('setModuleA'.'parameters')
Copy the code

Use mapMutations mapping in the component

import { mapMutations } from 'vuex'

methods: {
	...mapMutations([
		openFn: 'setModuleA'])}Copy the code

Mutations inside the module, the first parameter state accepted by default is the local state object of the module

const moduleA = {
	mutations: {
		setModuleA(state) {
			state.xxx += 'xxx'}}}Copy the code

If you need to take parameters

const moduleA = {
	mutations: {
		setModuleA(state, value) {
			state.xxx += value
		}
	}
}
Copy the code

8.5. Use actions in Modules to inject the module

Used directly in components

this.$store.dispatch('xxx')
Copy the code

Use mapActions mapping in the component

import { mapActions } from 'vuex'

methods: {
	...mapActions([
		'moduleA'])}Copy the code

Or rename it

import { mapActions } from 'vuex'

methods: {
	...mapActions({
		fn: 'moduleA'})}Copy the code

For actions within the module, the local state is exposed through context.state and the rootState is context.rootstate

const moduleA = {
  // ...
  actions: {
    fn ({ state, commit, rootState }) {
      if ((state.count + rootState.count) % 2= = =1) {
        commit('increment')}}}}Copy the code

8.6, namespaces

By default, actions, mutation, and getters within a module are registered in the global namespace, allowing multiple modules to respond to the same mutation or action. If you want your modules to be more encapsulation and reusable, you can make them namespaced by adding namespaced: True to them. When a module is registered, all its getters, actions, and mutations are automatically named according to the path the module was registered with.

8.6.1, use

First add namespaced: true to the moduleb.js module

const moduleB = {
	namespaced: true.state: () = > ({
		b: 'I am moduleB'
	}),
	mutations: {},
	actions: {},
	getters: {}}export default moduleB
Copy the code

In store index.js

import moduleA from './modules/moduleA'
import moduleB from './modules/moduleB'

export default new Vuex.Store({
	state: {},
	getters: {},
	mutations: {},
	actions: {},
	modules: {
		moduleA,
		moduleB
	}
})
Copy the code

If you use a namespace in a component, you need to use the same space name as mapState, mapGetters, mapMutations, and mapActions.

<script>
import { mapState, mapGetters, mapMutations } from 'vuex'

export default {
	name: 'Vuex'.data() {
		return{}},computed: {
		// Data for the moduleA module is injected here. mapState('moduleA'['a'
		]),
		// If you need to inject the moduleB module, write another one. mapState('moduleB'['b'])},mounted() {
		// Use it directly
		console.log(this.$store.state.moduleA.a)
		console.log(this.$store.state.moduleB.b)
	},
	methods: {}
}
</script>
Copy the code

8.6.2, access global content within a module with namespaces

If you want to use the global state and getter, rootState and rootGetters are passed into the getter as the third and fourth arguments, and the action is passed into the context object’s properties. To distribute action or commit mutation within the global namespace, pass {root: true} as the third argument to Dispatch or COMMIT

const moduleA = {
	namespaced: true.state: () = > ({
		a: 'I am moduleA'
	}),
	getters: {
		getModuleA(state, getters, rootState, rootGetters) {
			// Use state or getters for the global namespace
			return state.a + rootState.count
		}
	},
	mutations: {
		setModuleA(state) {
			console.log(state.a)
		}
	},
	actions: {
		addM({ state, commit, dispatch, rootState, rootGetters }) {
			console.log(rootState)
			console.log(rootGetters)
			// Call the global namespace method
			dispatch('rootFunction'.null, { root: true})}}}export default moduleA
Copy the code

Register global actions in namespaced modules

To register a global action in a namespaced module, add root: true and place the action definition in the handler function, where the first namespacedContext argument is the Context argument in the action

const moduleA = {
	namespaced: true.state: () = > ({
		a: 'I am moduleA'
	}),
	getters: {},
	mutations: {},
	actions: {
		rootFn: {
			root: true.handler(namespacedContext, param) {
				console.log(namespacedContext.state)
			}
		}
	}
}

export default moduleA
Copy the code

If you think it is helpful, I am @pengduo, welcome to like and follow the comments; END

The articles

  • Use NVM to manage node.js version and change NPM Taobao image source
  • Enumerative JS practical and powerful operator & operator
  • Wechat small program to achieve search keyword highlighting
  • Env files are used in vUE to store global environment variables and configure vUE startup and package commands
  • More detailed! Vue’s nine ways of communication

Personal home page

  • CSDN
  • GitHub
  • Jane’s book
  • Blog garden
  • The Denver nuggets