Review the Vuex

Vue-cli tool to create a project directly, check Vuex, other optional:

After creating the automatic installation dependency, start the project, write a simple demo to run the familiar helloWorld ~, then gradually implement a myVuex, to achieve the same desired result:

SRC/store/index. Js:

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

Vue.use(Vuex);

export default new Vuex.Store({
  state: {
    age: 7
  },
  getters: { // Say no more
    getAge(state) { return state.age }
  },
  mutations: { Vuex specifies that all operations on state should be placed here and triggered by the commit method
    changeAge(state, data) {
      state.age = data ? data : ++state.age
    }
  },
  actions: { Vuex specifies that asynchronous class functions are placed here, and the dispatch method is triggered
    syncChangeAge({ state, commit }, data) {
      state.age = 0
      setTimeout(() = > {
        this.commit('changeAge', data) / / I haven't find out what would stay here {commit} read, in the real Vuex without this also can run here
      }, 1000); }},modules: { /** Vuex modules */}});Copy the code

SRC/App. Vue:

<template> <div id="app"> {{ showMe }} <button @click="$store.commit("changeAge")">increase</button> <button @click="$store.dispatch("syncChangeAge", 7)">reset</button> </div> </template> <script lang="js"> import Vue from "vue";  Export default Vue. The extend ({name: "App", computed: {showMe () {return ` I'm ${this. $store. Getters. GetAge | | "..." At the age of} `; }}}); </script>Copy the code

The running effect is as follows:

Note: Click the “add” button to add a year old, click the “reset” button to enter loading state for 1 second, and then set it to 7 years old. Now, import Vuex from “Vuex” introduced in stroe. Change to their own manual implementation, to achieve the same performance as this demo.

Ready Perfect

To start, write the code structure, create a Vuex folder, and write the first index file.

SRC/Vuex/index. Js:

class Store {
    constructor(parameters) { // Vuex's core four pieces
        this.state = {}
        this.getters = {}
        this.mutations = {}
        this.actions = {}
    }
    get state() {
        return this.state
    }
    commit(fName, data) {
        this.mutations[fName].forEach(mutation= > mutation(data));
    }
    dispatch(fName, data) {
        this.actions[fName].forEach(action= >action(data)); }}export default { Store }
Copy the code

Now that the simple structure of vuEX is complete, the collection of mutation and actions passed into the instance is handled, and the commit and Dispatch functions are provided to execute them.

Create the install

Vue. Use (Vuex) is called in store to inject the state manager into Vue.

Mixin Reference: VUE global mixin

According to the vue documentation, you must provide an install function to use use, and vue is passed in as an argument, see: vueUse

SRC/Vuex/index. Js:

class Store {... }const install = (Vue) = > {
    Vue.mixin({
        beforeCreate() {
            const { store = null } = this.$options
            if (store) {
                this.$store = store
            } else {
                this.$store = this.$parent && this.$parent.$store
            }
        }
    })
}

export default { Store, install }
Copy the code

Binding state

When you created Install in the previous step, you introduced Vue, mounted it globally to create an instance object, and used bidirectional data binding in Vue to implement state:

SRC/Vuex/index. Js:

let _Vue

class Store {
    constructor(parameters) {
        const { state = { } } = parameters
        this.$vue = new _Vue({ // new A Vue instance receives the state passed in by the user
            data: { state }
        })
        ......
    }
    get state() { // Throws the state mounted on the Vue instance
        return this.$vue.state
    }
    ......
}

const install = (Vue) = >{ _Vue = Vue ...... }...Copy the code

Handling getter

Continue with the code above

.class Store {
    constructor(parameters){... bindInstall(this, parameters)
    }
    .....
}

const install = (Vue) = >{... }const bindInstall = (store, options) = > {
    / / processing getters
    const { getters } = options
    if (getters) {
        Object.keys(getters).forEach(key= > {
            Object.defineProperty(store.getters, key, {
                get() {
                    return getters[key](options.state)
                }
            })
        })
    }
}

export default { Store, install }
Copy the code

At this point, we can change the SRC /store/index import to our own:

// import Vuex from "vuex";
import Vuex from ".. /Vuex"; .Copy the code

Run the example and see that you have successfully gotten the getter in the store

Deal with mutations and actions

Continue with the bindInstall code:

.class Store {... }const install = (Vue) = >{... }const bindInstall = (store, options) = > { // Both collections are similar
    const { getters, mutations, actions } = options
    if (getters) { ... }
    if (mutations) {
        Object.keys(mutations).forEach(mutationName= > {
            let storeMutations = store.mutations[mutationName] || []
            storeMutations.push(data= > {
                mutations[mutationName].call(store, store.state, data) // mutations the first argument of the function is state, and the second is the value
            })
            store.mutations[mutationName] = storeMutations
        })
    }
    if (actions) {
        Object.keys(actions).forEach(actionName= > {
            let storeActions = store.actions[actionName] || []
            storeActions.push(data= > {
                actions[actionName].call(store, store, data) Vuex: vuex: vuex: vuex: vuex: vuex: vuex
            })
            store.actions[actionName] = storeActions
        })
    }
}
export default { Store, install }
Copy the code

Save, run the test – consistent with the initial demo results, and now implements the core VUex state manager

Here is the full Vuex/index.js code

let _Vue
class Store {
    constructor(parameters) {
        const { state = {} } = parameters
        this.$vue = new _Vue({ data: { state } })
        this.getters = {}
        this.mutations = {}
        this.actions = {}
        bindInstall(this, parameters)
    }
    get state() { return this.$vue.state }
    commit(fName, data) { this.mutations[fName].forEach(mutation= > mutation(data)) }
    dispatch(fName, data) { this.actions[fName].forEach(action= > action(data)) }
}
const install = (Vue) = > {
    _Vue = Vue
    Vue.mixin({
        beforeCreate() {
            const { store = null } = this.$options
            this.$store = store ? store : this.$parent ? this.$parent.$store : null}})}const bindInstall = (store, options) = > {
    const { getters, mutations, actions } = options
    if (getters) {
        Object.keys(getters).forEach(key= > {
            Object.defineProperty(store.getters, key, {
                get() { return getters[key](options.state) }
            })
        })
    }
    if (mutations) {
        Object.keys(mutations).forEach(mutationName= > {
            let storeMutations = store.mutations[mutationName] || []
            storeMutations.push(data= > { mutations[mutationName].call(store, store.state, data) })
            store.mutations[mutationName] = storeMutations
        })
    }
    if (actions) {
        Object.keys(actions).forEach(actionName= > {
            let storeActions = store.actions[actionName] || []
            storeActions.push(data= > { actions[actionName].call(store, store, data) })
            store.actions[actionName] = storeActions
        })
    }
}
export default { Store, install }
Copy the code