preface

First we need to know why vuex is used. Parent component communication prop and custom events can be handled, simple non-parent component communication bus (an empty Vue instance). Vuex, then, is used to solve complex communication between non-parent-child components.

Just knowing how to use Vuex is ok. You can read the documentation and tap the code. Don’t you want to know how vuex works? !

Leaving the vuex source code behind, let’s first think about how to implement a simple “vuex”. I don’t want getters, mutations, actions, etc. I just want state.

Non-parent component communication

Before implementing it, let’s review the implementation of bus, using the official website example:

var bus = new Vue()

// Triggers the event in component A
bus.$emit('id-selected'.1)

// Listen for events in the hooks created by component B
bus.$on('id-selected'.function (id) {
  // ...
})
Copy the code

Back then, I didn’t know where to put the instantiated bus, so I had no choice but to put it under Window and kept using window.bus. This is fine, but it still affects the global scope.

Then one day I found that I could mount it to the root instance of vue (goodbye window.bus) and here it was:

var app = new Vue({
  el: '#app'.bus: bus
})

/ / use the bus
app.$options.bus

// or
this.$root.$options.bus
Copy the code

It turns out that bus can communicate with more than just $emit and $ON events. Bus is actually a Vue instance where Data is responsive. For example, if there are two non-parent components under the app root instance, both of which use bus data, they are responsive to synchronization.

var bus = new Vue({
  data: {
    count: 0}})Copy the code

Above, child component A modifies count, and if child component B uses count, it responds to the latest count value.

All this, and you haven’t noticed? This is vuEX state for non-component communication. !

Enclosed bus

Yes, this is the simplest “vuex” (state only) to encapsulate the bus. First, we’ll have a root instance app with two non-parent components childA and childB.

The HTML code is implemented as follows:

<div id="app">
  <child-a></child-a>
  <child-b></child-b>
</div>
Copy the code

Implementation of non-parent components

Then there is the implementation of two non-parent components and app, each of which uses the count of bus, which is represented by store.state, as with vuex:

/ / implementation
const store = new Store(Vue, {
  state: {
  	count: 0}})// Subcomponent A
const childA = {
  template: '<button @click="handleClick">click me</button>'.methods: {
    handleClick () {
      this.$store.state.count += 1}}}// Subcomponent B
const childB = {
  template: '<div>count: {{ count }}</div>'.computed: {
    count () {
      return this.$store.state.count
    }
  }
}

new Vue({
  el: '#app'.components: {
    'child-a': childA,
    'child-b': childB
  },
  store: store
})
Copy the code

See there’s a Store waiting to be implemented in the code. The required parameters, since I am too lazy to use vue.use (), I pass Vue directly as a parameter for use, and then the second parameter is the same as the parameter we passed using vuex.

The realization of the Store

Next comes the Store implementation, which is implemented in two steps:

  1. Create a bus instance;
  2. Make this.$store accessible to all child components.

Step 1 is already there. Step 2 mainly uses vue. mixin to globalize mixin, but just finds the root instance with store and assigns $store to the Vue prototype. This ensures that the child instance is created without overwriting the $Store, and the root instance app does not have to write mixins to blend in.

class Store {
  constructor (Vue, options) {
    var bus = new Vue({
      data: {
      	state: options.state
      }
    })

    this.install(Vue, bus)
  }
  
  install (Vue, bus) {
    Vue.mixin({
      beforeCreate () {
        if (this.$options.store) {
          Vue.prototype.$store = bus
        }
      }
    })
  }
}
Copy the code

The implemented Store is a simple “vuex” with a Vuex state that is sufficient for simple communication between non-parent components.

Create a bus instance in the Store constructor and inject it into the Vue prototype so that all components can access this.$Store as bus instances. This.$store is a Vue instance, so accessing this.$store.state.count is actually accessing data, thus implementing response synchronization between non-parent components. All source code reference here.