Making, blog

The vue-router is simply implemented

Vuex

Vuex centrally manages the state of all components of an application and uses rules to ensure that the state changes in a predictable manner.

Install Vuex

vue add vuex
Copy the code

The core concept

  • State: indicates the status and data
  • Mutations: A function that changes state
  • Action: Indicates an asynchronous operation
  • Store: a container containing the preceding concepts

State – the state

State Saves the application state

export default new Vuex.Store({
  state: {
    counter: 0}})Copy the code
<h1>
  {{$store.state.counter}}
</h1>
Copy the code

Changes in state — mutations

Mutations are used to modify a state

export default new Vuex.Store({
  mutations: {add(state){
      state.counter++
    }
  }
})
Copy the code
<h1 @click="$store.commit('add')">
  {{$store.state.counter}}
</h1>
Copy the code

Derived state – getters

A new state is derived from state, similar to evaluating a property

export default new Vuex.Store({
  getters: {doubleCounter(state){
      return state.counter * 2; }}})Copy the code
<h1>
  {{$store.getters.doubleCounter}}
</h1>
Copy the code

Action – the actions

Add business logic, similar to controller

export default new Vuex.Store({
  actions: {add({commit}){
      setTimeout(() = > commit('add'), 1000); }}})Copy the code
<h1 @tap="$store.dispatch('add')">
  {{$store.state.counter}}
</h1>
Copy the code

Vuex principle analysis

Task analysis

  • Implement the plugin
    • To achieve the Store class
      • Maintain a responsive state state
      • Implement the commit ()
      • Realize the dispatch ()
      • Implement getters
    • Mount $store

Creating a new plug-in

In the SRC path of the vue2.x project, copy a store file and rename it ou-store.

Then create a new ou-vuex.js file in the ou-store path, and change vuex in the index.js file to ou-vuex.js.

import Vuex from './ou-vuex'
Copy the code

Also modify the router import in main.js.

import router from './ou-vuex'
Copy the code

Create a vUE plug-in

If you look back at store/index.js, you first registered Vuex with vue.use () and then instantiated the Vuex.Store class, so the Vuex object contains an install method and a store class.

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
    ...
})

Copy the code

So let’s create a new Vuex plug-in.

let Vue;    // Save the Vue constructor, which is needed in the plug-in

class Store {}

function install(_Vue) {
    Vue = _Vue;
}

export default {Store, install};
Copy the code

mount$store

let Vue;    // Save the Vue constructor, which is needed in the plug-in

class Store {}

function install(_Vue) {
    Vue = _Vue;

    Vue.mixin({
        beforeCreate() {
            / / mount $store
            if(this.$options.store){
                Vue.prototype.$store = this.$options.store;     // vm.$store}}})}export default {Store, install};
Copy the code

Implement the response type savestatedata

Since state is an object, we can use new Vue() to convert the state to reactive data and save it.

Second, we can’t explicitly save the state and expose it to the outside world, so we can use get and set to save it.

class Store {
    /* * options: * state * mutations * actions * modules * getters * */
    constructor(options = {}) {
        // data responds
        this._vm = new Vue({   
            data: {
                $$state: options.state    $$state or this._vm.$data.$$state}}); }/ / for the state
    get state() {
        return this._vm._data.$$state;
    }

    // State cannot be set
    set state(v) {
        console.error('please use replaceState to reset state'); }}Copy the code

implementationcommitmethods

When we use commit method, it is $store.com MIT (type,payload), the first parameter is type value of mutations, the second is payload, and the parameters corresponding to mutation method are state and payload, so we implement:

class Store {
    constructor(options = {}) {
        this._vm = new Vue({
            data: {
                $$state: options.state
            }
        });

        // Save the user configuration options for Mutations
        this._mutations = options.mutations;
    }

    get state() {
        return this._vm._data.$$state;
    }

    set state(v) {
        console.error('please use replaceState to reset state');
    }


    commit(type, payload) {
        // Obtain the mutation corresponding to type
        const entry = this._mutations[type]
        if(! entry) {console.error(`unknown mutation type : ${type}`);
            return ;
        }

        // Pass state and payload to mutation
        entry(this.state, payload)
    }
}
Copy the code

implementationdispatchmethods

The dispatch method is pretty much the same as the commit method, except that the dispatch method calls the action asynchronous function, and the action takes a context and a payload, and the payload is given to the dispatch argument, The context is actually this in the instance.

But actions are used to handle asynchronous functions, so we need to bind this to the Dispatch method; Also, the action method may call the COMMIT method, so we need to bind this to the COMMIT method as well.

class Store {
    constructor(options = {}) {
        this._vm = new Vue({
            data: {
                $$state: options.state 
            }
        });

        // Save the user configuration options for mutations and Actions
        this._mutations = options.mutations;
        this._actions = options.actions;

        // Bind the commit and Dispatch to this,
        this.commit = this.commit.bind(this);
        this.dispatch = this.dispatch.bind(this);
    }

    get state() {
        return this._vm._data.$$state;
    }

    set state(v) {
        console.error('please use replaceState to reset state');
    }


    commit(type, payload) {
        const entry = this._mutations[type]
        if(! entry) {console.error(`unknown mutation type : ${type}`);
            return ;
        }

        entry(this.state, payload)
    }

    dispatch(type, payload) {
        // Get the action corresponding to the type written by the user
        const entry = this._actions[type];
        if(! entry) {console.error(`unknown action type : ${type}`)}// Asynchronous result processing often requires a Promise to be returned
        return entry(this, payload)
    }
}
Copy the code

implementationgettersThe derived condition

When we define the state of getters, we are actually defining a function.

getters: {
   doubleCounter(state) {
     return state.counter * 2; }},Copy the code

When you use one of the derived states in getters, you actually get a value, which is the return value of the function.

<h4>double count: {{$store.getters.doubleCounter}}</h4>
Copy the code

This is actually a bit like the get property in an Object, so we can use object.defineProperty () to implement getters.

class Store {
    constructor(options = {}) {
        this._vm = new Vue({
            data: {
                $$state: options.state   
            }
        });

        this._mutations = options.mutations;
        this._actions = options.actions;

        this.commit = this.commit.bind(this);
        this.dispatch = this.dispatch.bind(this);

        // Initialize getters, which defaults to an empty object
        this.getters = {};

        / / traverse options. Getters
        for (const key in options.getters) {
            const self = this;
            Object.defineProperty(
                this.getters,
                key,   / / the key name
                {
                    get() {
                      	// Call the corresponding function, with the first argument being state, and return the result
                        return options.getters[key](self._vm._data.$$state)   
                    }
                }
            )
        }

    }
}
Copy the code