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 commitmutations
  • If asynchronous, dispatch.actionsThe essence is submissionmutations
  • How to triggeractions? You can use componentsVue ComponentsusedispatchOr the back-end interface to trigger
  • submitmutationsAfter 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 thedispatchandcommitMethod is replaced by arrow function to preventthisPointing 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.jscode

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


^ _ <