Quick start Vuex–> Write easy Vuex
preface
First of all, thank you for your support to Xiaolui during this period. Xiaolui will continue to work hard
Today’s article is about Vuex, you will not be unfamiliar with Vue
Today we’re going to learn about Vuex, then we’re going to talk about the basics, and then we’re going to implement a simple Vuex ourselves
Finally, I hope you can give xiao Lang a thumbs up
Past highlights:
From understanding the virtual DOM and implementing the DIff algorithm
Writing a simple VUE responsive style takes you through the principles of responsiveness
From use to implement their own simple Vue Router to see this line
The basics of a front end interview are small but you have to know them
1. Introduction
Vuex state management plug-in
The most important thing in Vue is data-driven and componentization. Each component has its own data,template and methods. Data is data, which is also called state, and the methods in methods change the state to update views. But in the actual development, when multiple components (and multi-layer components nested) share the same state, it will be very tedious to pass parameters. Here, we introduce Vuex to carry out state management, responsible for communication in components, and facilitate code maintenance
Vuex mainly solves the problem
- Multiple views depend on the same state
- Behaviors from different views need to change the same state
Benefits of using Vuex
- In the
vuex
Centralized management of shared data, easy to develop and later maintenance - It can realize data sharing between components efficiently and improve development efficiency
- in
vuex
The data in is responsive
2. Basic use of Vuex
First, add the Vuex plugin to Vue
After adding Vuex via vue-cli, there will be an extra store directory under the SRC directory of the project, and there will be an index.js directory under the directory
Vuex is also installed via NPM
npm install vuex --save
Copy the code
Turn on strict mode in the development environment so that modified data must be processed by mutation
You can set the environment in the package.json file scripts, and when we are in a development environment, you can turn on strict mode
The way to turn on strict mode is as simple as a single line of code
strict:products.env.NODE_ENV ! == 'production'
/* src/store/index.js */
/ / import Vue
import Vue from 'vue'
// Import the Vuex plugin
import Vuex from 'vuex'
// Register Vuex with Vue
Vue.use(Vuex)
export default new Vuex.Store({
// Turn on strict mode in the development environment so that data changes must be processed by mutationstrict:products.env.NODE_ENV ! = ='production'./ / state
state: {
},
// To handle the state
mutations: {
},
// For asynchronous processing
actions: {
},
// To mount the module
modules: {
}
})
Copy the code
To use the store, mount the store into Vue
Once the Store is mounted to Vue, all components can retrieve global data directly from the Store
import Vue from 'vue'
import App from './App.vue'
import store from './store'
Vue.config.productionTip = false
new Vue({
// Mount to vue
store,
render: (h) = > h(App),
}).$mount('#app')
Copy the code
1.state
Add data to state
The state we need to share is written in the state object
/* src/store/index.js */
/ / import Vue
import Vue from 'vue'
// Import the Vuex plugin
import Vuex from 'vuex'
// Register Vuex with Vue
Vue.use(Vuex)
export default new Vuex.Store({
state: {
name: 'Joe'.age: 21,},mutations: {},
actions: {},
modules: {},})Copy the code
Component to get the data in state
There are two ways to get a state
1. Use directlyThis. $store. State [properties]
, (this
Can be omitted)
<template>
<div id="app">
{{ this.$store.state.name }}
{{ this.$store.state.age }}
</div>
</template>
Copy the code
2. UsemapState
Mapping the Store to the component’s calculated properties using mapState is equivalent to having the properties in state inside the component
I know why we use… Expand it? We’ll see when we implement mapState
<template>
<div id="app">
{{ name }}
{{ age }}
</div>
</template>
<script>
// Import mapState from Vuex
import { mapState } from 'vuex'
export default {
name: 'App'.computed: {
// Map store to the computed properties of the current component. mapState(['name'.'age'])}}</script>
<style scoped>
</style>
Copy the code
Pay attention to
When the value in the store has the same state as the current component, we can pass an object in the mapState method instead of an array, and alias the state in the object
computed: {
// Name2 and age2 are aliases. mapState({name2: 'name'.age2: 'age'})}Copy the code
2.Mutation
The state in the Store cannot be operated directly, and we have to use Mutation to modify the state in the Store, which seems cumbersome, but convenient for centralized monitoring of data changes
Updates to state must be processed by mutations
Let’s now define a method in mutaions
If you want to define a method that can modify the state in the Store, the parameter is state
/* src/store/index.js */
/ / import Vue
import Vue from 'vue'
// Import the Vuex plugin
import Vuex from 'vuex'
// Register Vuex with Vue
Vue.use(Vuex)
export default new Vuex.Store({
state: {
name: 'Joe'.age: 21,},mutations: {
// Define the method here
/ * * * *@param {*} The first argument to state is the state in the Store (which must be passed) *@param {*} NewName is passed as an argument followed by multiple */
changeName(state, newName) {
// Here is a simple example to change the name
state.name = newName
},
},
actions: {},
modules: {},})Copy the code
The method in mutations is used in the components
There are also two methods for component trigger mutations
This.$store.com 1. MIT triggered ()
You define a method in methods, and in that method you trigger the method in mutations
<template>
<div id="app">
<button @click="handleClick">The Method 1 button uses the method in mutation</button>
{{ name }}
</div>
</template>
<script>
// Import mapState from Vuex
import { mapState } from 'vuex'
export default {
name: 'App'.computed: {
// Map store to the computed properties of the current component. mapState(['name'.'age'])},methods: {
handleClick() {
// Trigger mutations in changeName
this.$store.commit('changeName'.'the little waves')}}}</script>
<style scoped>
</style>
Copy the code
2. UsemapMutations
<template>
<div id="app">
<button @click="ChangeName (' the little waves')">The method 2 button uses the method in mutation</button>
{{ name }}
</div>
</template>
<script>
// Import mapState from Vuex
import { mapState, mapMutations } from 'vuex'
export default {
name: 'App'.computed: {
// Map store to the computed properties of the current component. mapState(['name'.'age'])},methods: {
// Map changeName method in Mutations to methods, and changeName can be directly used. mapMutations(['changeName'])}}</script>
<style scoped>
</style>
Copy the code
3.Action
Action and Mutation are different
Action is also used to process tasks, but it is used for asynchronous tasks. Asynchronous tasks must use Action, which triggers Mutation to change state indirectly, and cannot be used directly to modify asynchronous tasks
First define an asynchronous method in Action to call the method in Mutation
/* src/store/index.js */
/ / import Vue
import Vue from 'vue'
// Import the Vuex plugin
import Vuex from 'vuex'
// Register Vuex with Vue
Vue.use(Vuex)
export default new Vuex.Store({
state: {
name: 'Joe'.age: 21,},mutations: {
// Define the method here
/ * * * *@param {*} The first argument to state is the state in the Store (which must be passed) *@param {*} NewName is passed as an argument followed by multiple */
changeName(state, newName) {
// Here is a simple example to change the name
state.name = newName
},
},
actions: {
/ * * * *@param {*} Context The default parameter *@param {*} NewName passes its own argument */
// Define an asynchronous method where context is store
changeNameAsync(context, newName) {
// Asynchrony is simulated with setTimeout
setTimeout(() = > {
// Here we call the treatment in mutations
context.commit('changeName', newName)
}, 2000)}},modules: {},})Copy the code
There are two ways to use asynchronous methods in an Action within a component
1.this.$store.dispatch()
<template>
<div id="app">
<button @click="ChangeName2 (' the little waves')">The Method 1 button uses the method in action</button>
{{ name }}
</div>
</template>
<script>
MapState mapMutations is imported from Vuex
import { mapState, mapMutations } from 'vuex'
export default {
name: 'App'.computed: {
// Map store to the computed properties of the current component. mapState(['name'.'age'])},methods: {
changeName2(newName) {
// Use dispatch to call methods in actions
this.$store.dispatch('changeNameAsync', newName)
}
},
}
</script>
<style scoped>
</style>
Copy the code
2. UsemapActions
<template>
<div id="app">
<button @click="ChangeNameAsync (' the little waves')">The Mode 2 button uses the method in action</button>
{{ name }}
</div>
</template>
<script>
MapState mapMutations mapActions is imported from Vuex
import { mapState, mapMutations, mapActions } from 'vuex'
export default {
name: 'App'.computed: {
// Map store to the computed properties of the current component. mapState(['name'.'age'])},methods: {
// Map the methods specified in the Actions to methods, so you can use them directly in the component. mapActions(['changeNameAsync'])}}</script>
<style scoped>
</style>
Copy the code
4.Getter
Introduction to the
Getters are like calculating properties, but our data source is the state in Vuex, so we use the Getter in Vuex to do that
Application scenarios
You need to do some wrapper simplicity with state to show it in the view
So let’s write a Getter
/* src/store/index.js */
/ / import Vue
import Vue from 'vue'
// Import the Vuex plugin
import Vuex from 'vuex'
// Register Vuex with Vue
Vue.use(Vuex)
export default new Vuex.Store({
state: {
name: 'Joe'.age: 21,},getters: {
// Wrap the state here
/ * * * *@param {*} If you want to use the data in state, the first argument will be state by default, optionally named *@returns* /
decorationName(state) {
return 'Hello, everyone. My name is${state.name}This year,${state.age}At the age of `}},})Copy the code
And of course there are two ways that getters can be imported
1.This $store. Getters [name]
<template>
<div id="app">
{{ this.$store.getters.decorationName }}
</div>
</template>
Copy the code
2. UsemapGetters
<template>
<div id="app">
{{ decorationName }}
</div>
</template>
<script>
// Import mapGetters from Vuex
import { mapGetters } from 'vuex'
export default {
name: 'App'.computed: {
// Maps getters to evaluated properties of the current component. mapGetters(['decorationName'])}}</script>
Copy the code
5.Module
To avoid data bloat in a complex project state, Vuex allows the Store to be divided into different modules, each with its own state, getter, action, and mutation
Let’s create a new animal.js file here
/* animal.js */
const state = {
animalName: 'the lion',}const mutations = {
setName(state, newName) {
state.animalName = newName
},
}
/ / export
export default {
state,
mutations,
}
Copy the code
The module is mounted in modules in store/index.js
/* src/store/index.js */
/ / import Vue
import Vue from 'vue'
// Import the Vuex plugin
import Vuex from 'vuex'
// Import the module
import animal from './animal'
// Register Vuex with Vue
Vue.use(Vuex)
export default new Vuex.Store({
modules: {
animal,
},
})
Copy the code
Then we can use it in the component
<template>
<div id="app">
{{ this.$store.state.animal.animalName }}
<button @click="$store.com MIT ('setName', 'tiger ')">The name</button>
</div>
</template>
Copy the code
$store.state[name of the mounted module][properties in the mounted module]
This is a very complicated pattern
Adding a namespace
You can also use the mapXXX method to map, but write it a little differently. First, add a namespace namespaced: true to the exported one
/* animal.js */
const state = {
animalName: 'the lion',}const mutations = {
setName(state, newName) {
state.animalName = newName
},
}
export default {
// Use mapXXX after namespace convenience is enabled
namespaced: true,
state,
mutations,
}
Copy the code
Way 2
<template>
<div id="app">
{{ animalName }}
<button @click="The elegantly-named setName (' hawks')">The name</button>
</div>
</template>
<script>
MapState mapMutations is imported from Vuex
import { mapState, mapMutations } from 'vuex'
export default {
name: 'App'.computed: {
// mapState is used in a slightly different way. The first is the name of the module to which the module is mounted
// The second argument is the animal module state attribute. mapState('animal'['animalName'])},methods: {
MapMutations is also used in a slightly different way than before. The first is the name of the module to which the module is mounted
// The second parameter is the mutation method in the Animal module. mapMutations('animal'['setName'])}}</script>
Copy the code
3. Simulate a simple Vuex
Now that we have covered the basic use of Vuex, let’s write a simple Vuex ourselves
Code I will write full comments for you to watch, code very little, interested, you patiently watch, ( ̄▽ ̄) blue
1.index.js
Start with a basic frame
Let’s create our own Vuex folder under the SRC directory and add an index.js file to the directory where the Vuex we want to simulate will be
/* my-vuex/index.js */
// Save a global Vue that will be used later
let _Vue = null
/ / Store
class Store {
// First complete the constructor, which receives an object
constructor(options) {
/ /... To achieve}}// Since Vuex requires vue.use () to be installed, we must pass an install method to Vue
// The second argument is an optional object
function install(Vue) {
/ /... To achieve
}
// Export install and Store
export default {
install,
Store,
}
Copy the code
2. Install method
Since the Vuex plugin requires vue.use () to be installed, we must have an install method with the first argument passed to Vue
// The second argument is an optional object
function install(Vue) {
// Save to global _Vue
_Vue = Vue
// Global registration is mixed in so that all components can use $store
_Vue.mixin({
// beforeCreate Vue initialization phase
// Mount $store to Vue in beforeCreate
beforeCreate() {
// Determine whether the object passed by Vue has a store that needs to be mounted
// this.$options is the object passed by new Vue()
if (this.$options.store) {
// Mount the Store to the Vue prototype
_Vue.prototype.$store = this.$options.store
}
},
})
}
Copy the code
3. Let’s go ahead and implement the Store class
Complete the basic construction method first
/* my-vuex/index.js */
// Save a global Vue that will be used later
let _Vue = null
/ / Store
class Store {
// First complete the constructor, which receives an object
constructor(options) {
/ / initialization
const state = options.state || {}
const mutations = options.mutations || {}
const actions = options.actions || {}
const getters = options.getters || {}
}
/ /... install
Copy the code
Then, we come to realize the state, getters, mutations, the actions, commit, dispatch
(゚▽゚)/
3.state
Make state responsive by calling Vue observable directly
/* my-vuex/index.js */
/ / Store
class Store {
constructor(options) {
/ /... Other details
// 1. Implement state to transform the data in state into a response, directly using observable in Vue
this.state = _Vue.observable(state)
}
}
Copy the code
4.getters
Added a GET for each getters method
/* my-vuex/index.js */
/ / Store
class Store {
constructor(options) {
/ /... Other details
// create object.create (null); // create object.create (null);
// The benefit is that you don't have to worry about having the same name as the property on the prototype chain
this.getters = Object.create(null)
// We're going to add a get method to getters, so we're going to use data hijacking
// Get each method in getters first
Object.keys(getters).forEach((key) = > {
// The first argument is who to add, the second is the name of the property to add, and the third object can be set to many parameters
// For example, enumerable, configurable, get, set
Object.defineProperty(this.getters, key, {
// Add a get method for each item of this.getters
get: () = > {
// Remember that the getters method passes state in by default, changing the point to this
return getters[key].call(this.this.state)
},
})
})
}
}
Copy the code
5.mutations
I’m going to change this
/* my-vuex/index.js */
/ / Store
class Store {
constructor(options) {
/ /... Other details
// 3. Mutations
// First iterate through the mutaions to change this point
this.mutations = {}
Object.keys(mutations).forEach((key) = > {
this.mutations[key] = (params) = > {
// Change this point to pass state by default
mutations[key].call(this.this.state, params)
}
})
}
}
Copy the code
6.actions
Actually, it’s almost the same as mutations, but the parameters are passed differently, because context needs to be passed, which is an instance of Store, which is this
/* my-vuex/index.js */
/ / Store
class Store {
constructor(options) {
/ /... Other details
// 4. Implement actions
// As with mutations, we need to reorient this
this.actions = {}
Object.keys(actions).forEach((key) = > {
this.actions[key] = (params) = > {
// Change this point to pass in the store by default
actions[key].call(this.this, params)
}
})
}
}
Copy the code
7.commit
/* my-vuex/index.js */
/ / Store
class Store {
constructor(options) {
/ /... Other details
}
// 5. Implement the commit method
// The method used to trigger mutations
// The first argument is the event name and the second is the argument
commit = (eventName, params) = > {
this.mutations[eventName](params)
}
}
Copy the code
8.dispatch
Dispatch and COMMIT implementations are similar
/* my-vuex/index.js */
/ / Store
class Store {
constructor(options) {
/ /... Other details
}
// 6. Implement the dispatch method
// Used to trigger asynchronous methods in actions
// The first argument is the event name and the second is the argument
dispatch = (eventName, params) = > {
this.actions[eventName](params)
}
}
Copy the code
Well, almost here, a version of the Vuex was born, we write an example to test it
9. Test examples
Start with our own Vuex
/* src/store/index.js */
/ / import Vue
import Vue from 'vue'
// Import our own Vuex plugin
import Vuex from '.. /my-vuex/index'
// Install Vuex on Vue
Vue.use(Vuex)
export default new Vuex.Store({
state: {
name: 'Joe'.age: 21,},mutations: {
changeName(state, newName) {
// Here is a simple example to change the name
state.name = newName
},
},
actions: {
changeNameAsync(context, newName) {
// Asynchrony is simulated with setTimeout
setTimeout(() = > {
// Here we call the treatment in mutations
context.commit('changeName', newName)
}, 2000)}},getters: {
decorationName(state) {
return 'Hello, everyone. My name is${state.name}This year,${state.age}At the age of `}},})Copy the code
A simple VUE component
<template>
<div id="app">
<h1>{{this.$store.state.name}}</h1>
<h1>I am getters test: {{this. $store. Getters. DecorationName}}</h1>
<button @click="$store.com MIT ('changeName', 'mutations Button ')">Mutations button</button>
<button @click="$store.Dispatch ('changeNameAsync', 'Actions button ')">The actions button</button>
</div>
</template>
<script>
export default {
name: 'App',}</script>
<style scoped>
</style>
Copy the code
The mian. Js mount is the same as before
import Vue from 'vue'
import App from './App.vue'
import store from './store'
Vue.config.productionTip = false
new Vue({
// Mount to vue
store,
render: (h) = > h(App),
}).$mount('#app')
Copy the code
Example preview
This is not the end of it. Let’s simply implement a few mapXXX. They are all similar
10.mapSate
. MapSate ([‘age’,[‘name’]]), and computed is age: 21 and name: ‘three ‘, which can be used directly in components
const mapState = (params) = > {
// Here I just write the array alias
if (!Array.isArray(params))
throw new Error('Sorry, it's currently Vuex, only supports array arguments.')
// The first step is to initialize obj, otherwise [item] will report an error
let obj = {}
// The implementation logic is simple
// Go to this.$store
params.forEach((item) = > {
obj[item] = function() {
return this.$store.state[item]
}
})
return obj
}
Copy the code
11.mapMutations
const mapMutations = (params) = > {
// Here I just write the array alias
if (!Array.isArray(params))
throw new Error('Sorry, it's currently Vuex, only supports array arguments.')
// The first step is to initialize obj, otherwise [item] will report an error
let obj = {}
// The implementation logic is simple
// Go to this.$store
params.forEach((item) = > {
obj[item] = function(params) {
return this.$store.commit(item, params)
}
})
return obj
}
Copy the code
12.mapActions
const mapActions = (params) = > {
// Here I just write the array alias
if (!Array.isArray(params))
throw new Error('Sorry, it's currently Vuex, only supports array arguments.')
// The first step is to initialize obj, otherwise [item] will report an error
let obj = {}
// The implementation logic is simple
// Go to this.$store
params.forEach((item) = > {
obj[item] = function(params) {
return this.$store.dispatch(item, params)
}
})
return obj
}
Copy the code
13.mapGetters
const mapGetters = (params) = > {
// Here I just write the array alias
if (!Array.isArray(params))
throw new Error('Sorry, it's currently Vuex, only supports array arguments.')
// The first step is to initialize obj, otherwise [item] will report an error
let obj = {}
// The implementation logic is simple
// Go to this.$store
params.forEach((item) = > {
obj[item] = function() {
return this.$store.getters[item]
}
})
return obj
}
Copy the code
14. Export and use
And then the last thing is to derive
/ / export
export { mapState, mapMutations, mapActions, mapGetters }
Copy the code
Use the same method as before
<template>
<div id="app">
<button @click="ChangeName (' dog ')">mapMutations</button>
<button @click="ChangeNameAsync (' Dog 2 ')">mapMutations</button>
{{ decorationName }}
{{ age }}
</div>
</template>
<script>
/ / import
import { mapState, mapMutations, mapActions, mapGetters } from './my-vuex/index'
export default {
name: 'App'.computed: {
...mapState(['age']),
...mapGetters(['decorationName'])},methods: {
...mapMutations(['changeName']),
...mapActions(['changeNameAsync'])}}</script>. </style>Copy the code
3. The conclusion
Ok, this is the end of this article about Vuex, we from what Vuex is, how to use, start to achieve a simple Vuex we are all done, hope you have a harvest
Below the completion of our simulation of Vuex code posted, welcome everyone, a lot of communication, what write wrong place, please point out
simulationVuex
The complete code
/* my-vuex/index.js */
// Save a global Vue that will be used later
let _Vue = null
/ / Store
class Store {
// First complete the constructor, which receives an object
constructor(options) {
/ / initialization
const state = options.state || {}
const mutations = options.mutations || {}
const actions = options.actions || {}
const getters = options.getters || {}
// 1. Implement state to transform the data in state into a response, directly using observable in Vue
this.state = _Vue.observable(state)
// create object.create (null); // create object.create (null);
// The benefit is that you don't have to worry about having the same name as the property on the prototype chain
this.getters = Object.create(null)
// We're going to add a get method to getters, so we're going to use data hijacking
// Get each method in getters first
Object.keys(getters).forEach((key) = > {
// The first argument is who to add, the second is the name of the property to add, and the third object can be set to many parameters
// For example, enumerable, configurable, get, set
Object.defineProperty(this.getters, key, {
// Add a get method for each item of this.getters
get: () = > {
// Remember that the getters method passes state in by default, changing the point to this
return getters[key].call(this.this.state)
},
})
})
// 3. Mutations
// First iterate through the mutaions to change this point
this.mutations = {}
Object.keys(mutations).forEach((key) = > {
this.mutations[key] = (params) = > {
// Change this point to pass state by default
mutations[key].call(this.this.state, params)
}
})
// 4. Implement actions
// As with mutations, we need to reorient this
this.actions = {}
Object.keys(actions).forEach((key) = > {
this.actions[key] = (params) = > {
// Change this point to pass in the store by default
actions[key].call(this.this, params)
}
})
}
// 5. Implement the commit method
// The method used to trigger mutations
// The first argument is the event name and the second is the argument
commit = (eventName, params) = > {
this.mutations[eventName](params)
}
// 6. Implement the dispatch method
// Used to trigger asynchronous methods in actions
// The first argument is the event name and the second is the argument
dispatch = (eventName, params) = > {
this.actions[eventName](params)
}
}
// Since Vuex requires vue.use () to be installed, we must pass an install method to Vue
// The second argument is an optional object
function install(Vue) {
// Save to global _Vue
_Vue = Vue
// Global registration is mixed in so that all components can use $store
_Vue.mixin({
// beforeCreate Vue initialization phase
// Mount $store to Vue in beforeCreate
beforeCreate() {
// Determine whether the object passed by Vue has a store that needs to be mounted
// this.$options is the object passed by new Vue()
if (this.$options.store) {
// Mount the Store to the Vue prototype
_Vue.prototype.$store = this.$options.store
}
},
})
}
// mapState
const mapState = (params) = > {
// Here I just write the array alias
if (!Array.isArray(params))
throw new Error('Sorry, it's currently Vuex, only supports array arguments.')
// The first step is to initialize obj, otherwise [item] will report an error
let obj = {}
// The implementation logic is simple
// Go to this.$store
params.forEach((item) = > {
obj[item] = function() {
return this.$store.state[item]
}
})
return obj
}
// mapMutations
const mapMutations = (params) = > {
// Here I just write the array alias
if (!Array.isArray(params))
throw new Error('Sorry, it's currently Vuex, only supports array arguments.')
// The first step is to initialize obj, otherwise [item] will report an error
let obj = {}
// The implementation logic is simple
// Go to this.$store
params.forEach((item) = > {
obj[item] = function(params) {
return this.$store.commit(item, params)
}
})
return obj
}
// mapActions
const mapActions = (params) = > {
// Here I just write the array alias
if (!Array.isArray(params))
throw new Error('Sorry, it's currently Vuex, only supports array arguments.')
// The first step is to initialize obj, otherwise [item] will report an error
let obj = {}
// The implementation logic is simple
// Go to this.$store
params.forEach((item) = > {
obj[item] = function(params) {
return this.$store.dispatch(item, params)
}
})
return obj
}
// mapGetters
const mapGetters = (params) = > {
// Here I just write the array alias
if (!Array.isArray(params))
throw new Error('Sorry, it's currently Vuex, only supports array arguments.')
// The first step is to initialize obj, otherwise [item] will report an error
let obj = {}
// The implementation logic is simple
// Go to this.$store
params.forEach((item) = > {
obj[item] = function() {
return this.$store.getters[item]
}
})
return obj
}
/ / export
export { mapState, mapMutations, mapActions, mapGetters }
// Export install and store
export default {
install,
Store,
}
Copy the code