This chapter focuses on the implementation principle of VUEX,
Because the code is more comments, the code is more redundant, so it is best to download the source code, you can delete the comments to see, in fact, there is not much code
appetizers
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,
The state 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.
To give a brief indication of the official “one-way data flow” concept:
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. (also known as MVVM)
- You can’t just change the state in the store. The only way to change the state in the store is to commit mutations explicitly.
Look at the picture to understand the working principle:
If you understand this picture, you can see how VUex works
Points to note:
- The only way to change the state is to commit
mutations
- If asynchronous, dispatch.
actions
The essence is submissionmutations
- How to trigger
actions
? You can use componentsVue Components
usedispatch
Or the back-end interface to trigger - submit
mutations
After that, components can be dynamically renderedVue Components
Do you think there is something missing? Yes, it’s getters saying when implementing the following principle
Principle of implementation
The preparatory work
First, delete all unnecessary files and codes, and classical structure, as follows:
App. Vue code:
<template> <div> <! </div> </template> <script> export default {name:'app',} </script>Copy the code
The main. Js code:
import Vue from 'vue' import App from './App.vue' import store from './store' import router from 'vue-router' Vue. Config. productionTip = false new Vue({name:'main', router, router-view router-link $router $route store, Render: h => h(App)}).$mount('# App ')Copy the code
Store. Js code:
Import Vue from 'Vue' import Vue from 'Vue' import Vue from 'Vue' import Vue from 'Vue' Vuex {install Store} import vuex from './vuex' vue.use (vuex) // Need to create a repository and export // when new, a bunch of stuff was passed into vuex.js Export default new vuex. Store({state:{name:'Fan'}, //getters :{state:{name:'Fan'}, Mutations :{}, actions:{}})Copy the code
Instead of writing the code in the vuex.js file, let’s start
To implement the state
Now that we’re done, let’s implement our state
Write the following code in vuex.js (detailed instructions and operations are commented in the code) :
// define a Vue that can be used globally. Class Store{// when new, a bunch of stuff is passed to vuex.js, which needs to be received using constructor(options){// console.log(options); // Print out {state: {... }, getters: {... }, mutations: {... }, actions: {... }}, You can get the data inside the / * -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - the state principle -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - * / // Attach a state to each component's $store, $store. State this.state = options.state // Pass a name above state :'Fan' print // console.log(this.state); // Print the result {name: "Fan"} /*-------------------------------------------------------------------------------------------------*/ } } //install is essentially a function const install = (_Vue)=>{// console.log('...... '); // Assign constructor to global Vue Vue = _Vue; Mixin ({beforeCreate() {console.log(this.$options.name) //this for each component, test, }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}} If (this.$options && this.$options.store){this.$store = this.$options.store}else{// If this. Also hang $store on it, because it's a tree component, // App.vue mounted({console.log(this.$store)}); // App.vue mounted({console.log(this.$store)}); // App.vue mounted({console.log(this. Export default {install, store} export default {install, store}Copy the code
In this case, all components can use this.$store.state
Implement getters
First define two methods in getters in store.js to test:
//getters is a method, but can be used as a property getters:{ Namely, computed myName(state){return state.name+'Jun'}, myAge(){}},Copy the code
We then write our code from the Store class constructor in the vuex.js file as follows:
Class Store{// when new, a bunch of stuff is passed to vuex.js, which needs to be received using constructor(options){// console.log(options); // Print out {state: {... }, getters: {... }, mutations: {... }, actions: {... }}, You can get the data inside the / * -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - the state principle -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - * / // Attach a state to each component's $store, $store. State // this.state = options.state / * -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - * / / * -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- state of reactive principle -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - * / / / the above method is not perfect, Can't render dynamically when changing data, _s = new Vue({data:{// Only data in data is responsive state:options.state}}) // Pass a name above state :'Fan' print // console.log(this.state); // Print the result {name: "Fan"} /* ------------------------------------------------------------------------------------------------ */ / * -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- getters principle -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - * / / / to get the getters warehouse, if people don't write getters, will default to empty the let getters = options. The getters | | {} / / console log (getters); // Print out an object containing a method {myName: ƒ} // Mount getters = {this.getters = { // Write a method myAge to store. Js getters to test // console.log(object.keys (getters)); Keys (getters).foreach ((getter)=>{// console.log(getter); // Print myName myAge Object.defineProperty(this.getters,getter,{// Get is automatically called when you want to get the getter. // Always use arrow function, Get ()=>{console.log(this); return getters[getter](this.state) } }) }) /*-------------------------------------------------------------------------------------------------*/ } get state(){ return this._s.state } }Copy the code
Then test it in app.vue:
<template> <div> <! {{this.$store.state.name}} <! {{this.$store.state.name}} <! {{this.$store.getters.myName}} <! </div> </template> <script> export default {name:'app', mounted(){console.log(this.$store); } } </script>Copy the code
Realization of mutations
Try someone else’s first:
Define an add method in app. vue with a button to trigger the method:
<template> <div> <! {{this.$store.state.name}} <! {{this.$store.state.name}} <! {{this.$store.getters.myName}} <! <button @click="add()"> add </button> </div> </template> <script> export default { name:'app', mounted(){ console.log(this.$store); }, methods:{add(){//commit a mutations this.store.com ('add',10)}}Copy the code
Using vuex in store.js:
import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) export default new Vuex.Store({ state:{ name:'Fan', Getters :{age:10}, getters:{age:10}, getters:{age:10} Computed myName(state){return state.name+'Jun'}, myAge(){}}, // Change the state: Mutations :{add(state,payload){state.age += payload}},})Copy the code
This time, when you click the Add button, you can Add 10
Then write yourself:
Write mutations in store.js and define two methods:
Mutations :{add(state,payload){state.age += payload}, sub(){}},Copy the code
Then implement it in the vuex.js class Store:
Class Store{// when new, a bunch of stuff is passed to vuex.js, which needs to be received using constructor(options){// console.log(options); // Print out {state: {... }, getters: {... }, mutations: {... }, actions: {... }}, You can get the data inside the / * -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - the state principle -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - * / // Attach a state to each component's $store, $store. State // this.state = options.state / * -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - * / / * -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- state of reactive principle -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - * / / / the above method is not perfect, Can't render dynamically when changing data, //_s = new Vue({data:{// Only data in data is responsive state:options.state}}) // Pass a name above state :'Fan' print // console.log(this.state); // Print the result {name: "Fan"} /* -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - * / / * -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- getters principle -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - * / / / to get the getters warehouse, if people don't write getters, will default to empty the let getters = options. The getters | | {} / / console log (getters); // Print out an object containing a method {myName: ƒ} // Mount getters = {this.getters = { // Write a method myAge to store. Js getters to test // console.log(object.keys (getters)); Keys (getters).foreach ((getter)=>{// console.log(getter); // Print myName myAge Object.defineProperty(this.getters,getter,{// Get is automatically called when you want to get the getter. // Always use arrow function, Get ()=>{// console.log(this); return getters[getter](this.state) } }) }) /* -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - * / / * -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- mutatios principle -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - * / / / and getters / / almost get mutations let mutations = options. The mutations | | {} / / console log (mutations); //{add: Faraway = = = = = = = = = = = = = = = = = = = Mutations [mutations] = (payload)=>{mutations[mutations](this.state,payload)}}) Right. / / the console log (mutations); / / {add: ƒ, sub: ƒ} // But he was sick, Need to implement a commit, in the following implementation / * -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- Commit (type,payload){//{add: ƒ, sub: ƒ} return return this._s.state} return return this._s.state}Copy the code
Test in app.vue:
<template> <div> <! {{this.$store.state.name}} <! {{this.$store.state.name}} <! {{this.$store.getters.myName}} <! <button @click="add()"> add </button> </div> </template> <script> export default { name:'app', mounted(){ // console.log(this.$store); }, methods:{add(){//commit a mutations this.store.com ('add',10)}}Copy the code
Because the code is a little bit redundant, I’ve simplified the code by putting the common methodObject.keys(obj).forEach(key => { callback(key, obj[key]) })
Pull it out.
You can download the source code to have a look, here is not to say
To implement the actions
Again, in vuex.js class Store, because I’m simplifying the code, so let’s copy the whole thing and see,
Here is thedispatch
andcommit
Method is replaced by arrow function to preventthis
Pointing to something wrong
// define a Vue that can be used globally. // forEach is used to loop through an object const forEach = (obj, ----> callback object.keys (obj). ForEach (key => {callback(key, Obj [key])})} class Store {// When new, a bunch of stuff is passed to vuex.js, Constructor (options) {// console.log(options); // Print out {state: {... }, getters: {... }, mutations: {... }, actions: {... }}, You can get the data inside the / * -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - the state principle -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - * / // Attach a state to each component's $store, $store. State // this.state = options.state / * -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - * / / * -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- state of reactive principle -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - * / / / the above method is not perfect, _s = new Vue({data: {// Only data in data is responsive state: {// Only data in data is responsive state: {// Only data in data is responsive state: options.state } }) /* -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - * / / * -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- getters principle -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - * / // Pass a name above state :'Fan' print // console.log(this.state); / / print results {name: "Fan"} / / to get the getters warehouse, if people don't write getters, will default to empty the let getters = options. The getters | | {} / / console log (getters); // Print out an object containing a method {myName: ƒ} // Mount getters = {this.getters = { // Write a method myAge to store. Js getters to test // console.log(object.keys (getters)); // Print out ["myName", "myAge"] forEach(getters, (getterName, value) => {object.defineProperty (this.defineProperty, getterName, { get: () => { return value(this.state) } }) }) /* -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - * / / * -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- mutatios principle -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - * / / / and getters / / almost get mutations let mutations = options. The mutations | | {} / / console log (mutations); //{add: ƒ} // open this. Mutations = {} Mutations [mutationName] = (payload) => {value(this.state, payload)}}) Right. / / the console log (mutations); / / {add: ƒ, sub: ƒ} // But he needs to implement commit, implement /* below -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - * / / * -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- the actions principle -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - * / / / and the above two kinds of the same, not much comment let the actions = options. Actions | | {} this. Actions = {}; forEach(actions, (action, value) => { this.actions[action] = (payload) => { value(this, payload) } }) /* -------------------------------------------------------------------------------------------------- */ } Dispatch = (type, payload) => {this.actions[type](payload)} Commit = (type, payload) => {//{add: ƒ, sub: ƒ} this. Mutations [type](payload)} get state() {return this._s.state}} const install = (_Vue) => { // console.log('...... '); // Assign constructor to global Vue Vue = _Vue; Mixin ({beforeCreate() {console.log(this.$options.name) //this for each component, test, }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}} If (this.$options && this.$options.store) {this.$store = this.$options.store} else {// If this. // App.vue mounted(); // App.vue mounted(); // App.vue mounted(); // App.vue mounted() Export default {install, store} export default {install, store}Copy the code
Add an asynchronous method to mutations:
mutations: {
add(state, payload) {
state.age += payload
},
sub() {
},
asyncSub(state, payload) {
state.age -= payload
}
},
Copy the code
Write an actions in store.js
actions: {
asyncSub({commit}, payload) {
setTimeout(() => {
commit("asyncSub", payload)
}, 2000)
}
}
Copy the code
Finally, define method tests in app.vue:
<template> <div> <! {{this.$store.state.name}} <! {{this.$store.state.name}} <! {{this.$store.getters.myName}} <! - print out FanJun - > < hr > {{this. $store. State. The age}} <! <button @click="add"> add </button> <! <button @click="sub">Async sub </button> </div> </template> <script> export default {name: "app", mounted() { // console.log(this.$store); SetTimeout (() => {this.$store.state.age = 666; }, 1000); // console.log(this.$store.state); }, methods: {add() {return mutations this.upgrades.store.com MIT ("add", 10); }, sub(){ this.$store.dispatch("asyncSub",10) } } }; </script>Copy the code
expungedvuex.js
code
There really isn’t much code
let Vue;
const forEach = (obj, callback) => {
Object.keys(obj).forEach(key => {
callback(key, obj[key])
})
}
class Store {
constructor(options) {
this._s = new Vue({
data: {
state: options.state
}
})
let getters = options.getters || {}
this.getters = {};
forEach(getters, (getterName, value) => {
Object.defineProperty(this.getters, getterName, {
get: () => {
return value(this.state)
}
})
})
let mutations = options.mutations || {}
this.mutations = {};
forEach(mutations, (mutationName, value) => {
this.mutations[mutationName] = (payload) => {
value(this.state, payload)
}
})
let actions = options.actions || {}
this.actions = {};
forEach(actions,(actionName,value)=>{
this.actions[actionName] = (payload)=>{
value(this,payload)
}
})
}
dispatch=(type,payload)=>{
this.actions[type](payload)
}
commit=(type, payload)=>{
this.mutations[type](payload)
}
get state() {
return this._s.state
}
}
const install = _Vue => {
Vue = _Vue
Vue.mixin({
beforeCreate() {
if (this.$options && this.$options.store) {
this.$store = this.$options.store
} else {
this.$store = this.$parent && this.$parent.$store
}
}
})
}
export default { install, Store }
Copy the code
An overview
Because there are too many comments, it is very complicated, so it is best to download the source code and try to write it yourself
Attached source code address:Vuex implementation principle