How to write a simple Vuex by hand

  • 2021-08-28 19:20:39
  • OfStack

Preface

This article is suitable for people who have used Vuex to read and understand how to implement an Vuex by themselves.

Basic skeleton

This is the src/store/index. js file of this project. Look at the use of vuex


import Vue from 'vue'
import Vuex from './myvuex' //  Introduce your own writing  vuex
import * as getters from './getters'
import * as actions from './actions'
import state from './state'
import mutations from './mutations'

Vue.use(Vuex) // Vue.use(plugin) Method uses vuex Plug-ins 

// vuex  Export 1 A class is called Store And pass in an object as a parameter 
export default new Vuex.Store({
 state,
 mutations,
 actions,
 getters,
})

Vue.use Usage of:

Install the Vue. js plug-in. If the plug-in is a 1 object, the install method must be provided. If the plug-in is a function, it will be used as an install method. When the install method is called, Vue is passed in as a parameter. The first parameter of this method is the Vue constructor, and the second parameter is an optional option object.

This method needs to be called before calling new Vue (). When the install method is called multiple times by the same plug-in, the plug-in will only be installed once.

That is, we need to ./myvuex.js Export in install Method, exporting 1 class at the same time Store So step 1 can write the code:


let Vue = null

class Store {
 constructor(options) {}
}

function install(_Vue) {
 Vue = _Vue //  Above Store Class needs to be able to get the Vue
}

export default {
 Store,
 install,
}

install method

When we use vuex, each component has an this. $store attribute, which contains state, mutations, actions, getters, etc. Therefore, we also need to mount an $store attribute on each component, so that every component can get it. Here we use Vue. mixin (mixin), and the usage is introduced as follows:

Global registration of 1 mixed, affecting all Vue instances created after registration. You can use blending to inject custom behavior into the component, which affects every Vue instance created later.


function install(_Vue) {
 Vue = _Vue // install Method is called, the Vue Pass in as a parameter (above Store Class requires the Vue ) 
 //  Achieve every 1 Components, all of which can pass this Call $store
 Vue.mixin({
  beforeCreate() {
   //  Pass this.$options Can get new Vue({ Parameter })  Parameters passed 
   if (this.$options && this.$options.store) {
    //  Prove this this Is the root instance, which is new Vue The instance generated 
    this.$store = this.$options.store
   } else if (this.$parent && this.$parent.$store) {
    //  Child component gets the parent component's $store Attribute 
    this.$store = this.$parent.$store
   }
  },
 })
}

state

Since Vuex is based on the responsive principle of Vue, we need to create an instance of vue to make the data change to the refreshable view


class Store {
 // options  That is  Vuex.Store({}) Parameters passed in 
 constructor(options) {
  // vuex  The core is to borrow vue Because of the instance of vue The view is refreshed when the instance data of the 
  let vm = new Vue({
   data: {
    state: options.state,
   },
  })
  // state
  this.state = vm.state
 }
}

commit

When we use vuex to change data, we trigger the commit method, which is used as follows:

this. $store. commit ('eventName', 'Parameters');

So we want to implement an commit method and process the mutations passed in by the Store constructor


class Store {
 constructor(options) {
  //  Realization  state ...

  // mutations
  this.mutations = {} //  Store incoming mutations
  let mutations = options.mutations || {}
  //  Loop out the event name for processing ( mutations[ Event name ]:  Execution method) 
  Object.keys(mutations).forEach(key => {
   this.mutations[key] = params => {
    mutations[key].call(this, this.state, params) //  Amendment this Point 
   }
  })
 }

 commit = (key, params) => {
  // key Is the name of the event to fire 
  this.mutations[key](params)
 }
}

dispatch

Same as the commit process above


class Store {
 constructor(options = {}) {
  // ...

  // actions
  this.actions = {}
  let actions = options.actions || {}
  Object.keys(actions).forEach(key => {
   this.actions[key] = params => {
    actions[key].call(this, this, params)
   }
  })
 }

 dispatch = (type, payload) => {
  this.actions[type](payload)
 }
}

getters

getters actually returns the value of state, which is placed in the computed attribute when used, and every getter is in the form of a function;

getters requires bidirectional binding. But you don't need to bind all getters bidirectionally, just bind the getters used by events in your project.

The Object. defineProperty () method is used here, which either defines a new property on an object directly, or modifies an existing property on an object and returns the object.


class Store {
 constructor(options = {}) {
  // ...

  // getters
  this.getters = {}
  let getters = options.getters || {}
  Object.keys(getters).forEach(key => {
   Object.defineProperty(this.getters, key, {
    get: () => {
     return getters[key].call(this, this.state)
    },
   })
  })
 }
}

So far, we can do some basic operations with our own vuex, but we can only call it in the form of this. $store. xx, so we need to implement the method again.

map auxiliary function

Let's talk about mapState first

This is used before there is no map helper function:


computed: {
 count () {
  return this.$store.state.count
 }
}

Pass an array of strings to mapState when the name of the mapped evaluated attribute is the same as the child node name of state.


computed: {
 //  Use the object expansion operator to mix this object into an external object 
 ...mapState(['count'])
}

We simply implement the array case here


export const mapState = args => {
 let obj = {}
 args.forEach(item => {
  obj[item] = function() {
   return this.$store.state[item]
  }
 })
 return obj
}

The next few map helper functions are similar

mapGetters

let Vue = null

class Store {
 constructor(options) {}
}

function install(_Vue) {
 Vue = _Vue //  Above Store Class needs to be able to get the Vue
}

export default {
 Store,
 install,
}
0 mapMutations

let Vue = null

class Store {
 constructor(options) {}
}

function install(_Vue) {
 Vue = _Vue //  Above Store Class needs to be able to get the Vue
}

export default {
 Store,
 install,
}
1 mapActions

let Vue = null

class Store {
 constructor(options) {}
}

function install(_Vue) {
 Vue = _Vue //  Above Store Class needs to be able to get the Vue
}

export default {
 Store,
 install,
}
2

Complete code


let Vue = null

class Store {
 constructor(options) {
  // vuex  The core is to borrow vue Because of the instance of vue The view is refreshed when the instance data of the 
  let vm = new Vue({
   data: {
    state: options.state,
   },
  })
  // state
  this.state = vm.state

  // mutations
  this.mutations = {} //  Store incoming mutations
  let mutations = options.mutations || {}
  Object.keys(mutations).forEach(key => {
   this.mutations[key] = params => {
    mutations[key].call(this, this.state, params)
   }
  })

  // actions
  this.actions = {}
  let actions = options.actions || {}
  Object.keys(actions).forEach(key => {
   this.actions[key] = params => {
    actions[key].call(this, this, params)
   }
  })

  // getters
  this.getters = {}
  let getters = options.getters || {}
  Object.keys(getters).forEach(key => {
   Object.defineProperty(this.getters, key, {
    get: () => {
     return getters[key].call(this, this.state)
    },
   })
  })
 }

 commit = (key, params) => {
  this.mutations[key](params)
 }

 dispatch = (type, payload) => {
  this.actions[type](payload)
 }
}

export const mapState = args => {
 let obj = {}
 args.forEach(item => {
  obj[item] = function() {
   return this.$store.state[item]
  }
 })
 return obj
}

export const mapGetters = args => {
 let obj = {}
 args.forEach(item => {
  obj[item] = function() {
   return this.$store.getters[item]
  }
 })
 return obj
}

export const mapMutations = args => {
 let obj = {}
 args.forEach(item => {
  obj[item] = function(params) {
   return this.$store.commit(item, params)
  }
 })
 return obj
}

export const mapActions = args => {
 let obj = {}
 args.forEach(item => {
  obj[item] = function(payload) {
   return this.$store.dispatch(item, payload)
  }
 })
 return obj
}

function install(_Vue) {
 Vue = _Vue // install Method is called, the Vue Pass in as a parameter (above Store Class requires the Vue ) 
 //  Achieve every 1 Components, all of which can pass this Call $store
 Vue.mixin({
  beforeCreate() {
   //  Pass this.$options Can get new Vue({ Parameter })  Parameters passed 
   if (this.$options && this.$options.store) {
    //  Prove this this Is the root instance, which is new Vue The instance generated 
    this.$store = this.$options.store
   } else if (this.$parent && this.$parent.$store) {
    //  Child component gets the parent component's $store Attribute 
    this.$store = this.$parent.$store
   }
  },
 })
}

export default {
 Store,
 install,
}

The above is how to handwrite a simple Vuex details, more about handwritten vuex information please pay attention to this site other related articles!


Related articles: