When I first learned about eventBus and VUex, I was not clear about their principles, only that they were not brain useful, and sometimes I was not very clear when using them. Recently, I was curious about the difference between the two, so I would like to summarize them in this article

eventBus

define

  • Also known as the event bus, as a common event center components
  • Components can inform other components in parallel

The working principle of

  • Publish subscribe model
  • Create a vUE instance and use an empty VUE instance as a bridge to communicate between vUE components

disadvantages

  • It’s not very convenient and can be a disaster to maintain if used carelessly, which is why a more sophisticated VUEX as a state manager is needed to take the concept of notifications up to the level of shared state

use

  • Initialize the
    • Methods a
    // event-bus.js
    import Vue from 'vue'
    export const EventBus = new Vue()
    Copy the code
    • Method 2

    Create a global event bus

    // main.js
    Vue.prototype.$EventBus = new Vue()
    Copy the code
  • A common data or method can be maintained
export default new Vue({
    data() {
        return {
            searchShow: true,
            showBtn: true
        }
    },
    methods: {
        searchShowChange(value) {
            if (typeof value === 'boolean') {
                this.searchShow = false
            } else{ this.searchShow = ! this.searchShow } this.$emit('searchShowChange')}}})Copy the code
  • $on/$emit/$off
    • $emit sends an event
    • $ON Receives events
    • $off Removes the listener
      • It’s very important, if you don’t manually clear it, it’s always there, and you go back and forth into the receiving component and you get the data multiple times instead of once
      • Remove single event eventbus. $off(‘aMsg’)
      • Remove all events eventbus. $off()
     beforeDestroy() {// Unbind the event before the component is destroyed. Otherwise, the problem of repeatedly firing events is this.bus.$off(this.$route.path);
     }
    Copy the code

VUEX

background

The simplicity of one-way data flow is easily broken when our application encounters state shared by multiple components: 1. Multiple views depend on the same state. 2. Behaviors from different views need to change the same state.

For problem one, the pass-throughs method would be cumbersome for multiple nested components and would not be able to handle state passing between sibling components. For problem two, we often use parent-child components to refer directly or to change and synchronize multiple copies of the state through events. These patterns are very fragile and often result in unmaintainable code.

The working principle of

  • Using the singleton pattern in the JS design pattern, what the singleton pattern tries to do is that no matter how many times we try to create it, it only returns you the single instance that was created the first time.
  • In this mode, our tree of components forms a huge “view” where any component can get state or trigger behavior no matter where it is in the tree!
  • By defining and isolating the various concepts in state management and enforcing rules to maintain the independence between view and state, our code will become more structured and maintainable.

define

Is a state management pattern developed specifically for vue.js applications. It uses centralized storage to manage the state of all components of an application, and rules to ensure that the state changes in a predictable manner.

use

state
  • Returns a state in a computed property

Disadvantages: Frequent imports and the need to simulate state when testing components

computed: {
    count () {
      return store.state.count
    }
}
Copy the code
  • “Inject” from the root component into each child component with the Store option (call vue.use (Vuex))

The child component can be accessed through this.$store

const app = new Vue({
  el: '#app'// Provide the Store object to the "Store" option, which injects instances of the Store into all child components store, Components: {Counter}, template: '<div class="app">
      <counter></counter>
    </div>
  `
})

const Counter = {
  template: `<div>{{ count }}</div>`,
  computed: {
    count () {
      return this.$store.state.count
    }
  }
}
Copy the code
  • MapState helper function
MapState import {mapState} from; // The helper function is vuex. mapState import {mapState} from'vuex'

exportdefault { // ... Computed: mapState({// Arrow function to simplify code count: state => state.count, // pass string parameters'count'Equivalent to 'state => state.count' countAlias:'count'In order to get local state using 'this', we must use the regular function countPlusLocalState (state) {return state.count + this.localCount
    }
  })
}
Copy the code
  • So we pass mapState an array of strings
Computed: mapState([// map this.count to store.state.count'count'
])
Copy the code
  • The object expansion operator is mixed with locally computed properties
computed: {
  localComputed() {/ *... */}, // use the object expansion operator to mix this object into an external object... mapState({ // ... })}Copy the code
Getter
  • Usage scenarios
    • Derive states from the State in store, such as filtering and counting lists
  • Access in the form of properties
store.getters.doneTodos
Copy the code
  • Take two parameters: state/getters
getters: {
  // ...
  doneTodosCount: (state, getters) => {
    return getters.doneTodos.length
  }
}
Copy the code
  • Access by method

Note that the getter is called every time it is accessed through a method, and the result is not cached.

getters: {
  // ...
  getTodoById: (state) => (id) => {
    return state.todos.find(todo => todo.id === id)
  }
}
store.getters.getTodoById(2) // -> { id: 2, text: '... '.done: false }
Copy the code
  • MapGetters helper function

The mapGetters helper function simply maps getters in a store to locally computed properties

import { mapGetters } from 'vuex'

exportdefault { // ... Computed: {// Use the object expansion operator to mix getters into computed objects... mapGetters(['doneTodosCount'.'anotherGetter', / /... ] }} // If you want to give a getter property another name, use the object form... MapGetters ({// map 'this.doneCount' to 'this.$store.getters.doneTodosCount`
  doneCount: 'doneTodosCount'
})
Copy the code
Mutations
  • Pass an extra parameter, the payload of the mutation, to store.com MIT

In most cases, the payload should be an object so that it can contain multiple fields and that the mutation recorded is more readable

mutations: {
  increment (state, payload) {
    state.count += payload.amount
  }
}

store.commit('increment', {amount: 10}) store.com MIT ({amount: 10})type: 'increment',
  amount: 10
})
Copy the code
  • Commit the Mutation in the component

    • this.$store.commit(‘xxx’)
    • usemapMutationsHelper functions map the methods in the component to calls to store.com MIT
      import { mapMutations } from 'vuex'
      
      exportdefault { // ... methods: { ... mapMutations(['increment', // maps' this.increment() 'to' this.$store.commit('increment'MapMutations' also supports load:'incrementBy'// Map 'this.incrementby (amount)' to 'this.$store.commit('incrementBy', amount)` ]), ... mapMutations({ add:'increment'// Map 'this.add()' to 'this.$store.commit('increment') `}}}Copy the code
  • Matters needing attention

    • It is best to initialize all required properties in your store in advance.
    • When you need to add new properties to an object, you should useVue.set(obj, 'newProp', 123), or replace the old object with a new one.

      For example, with the object expansion operator we can write: state.obj = {… state.obj, newProp: 123 }

  • Replace the Mutation event type with constants

1. Constants are easier to avoid errors. If you assign a value to a constant in a program that already has a value, the compiler reports an error. 2. It is convenient for unified management when many people cooperate with each other. In addition, when methods are invoked in state management, the mutation method can be identified at a glance, and problems can be found directly and quickly.

// mutation-types.js
export const SOME_MUTATION = 'SOME_MUTATION'

// store.js
import Vuex from 'vuex'
import { SOME_MUTATION } from './mutation-types'const store = new Vuex.Store({ state: { ... Mutations: {// Mutations: {// mutations: {// mutations: {// mutations: {// mutations: {// mutations: {// mutations: {// mutations: {// mutations: {// mutations: {// mutations: {// mutations: {// mutations:}}})Copy the code
Actions
  • vs Mutations
    • The Action commits the mutation instead of directly changing the state.
    • An Action can contain any asynchronous operation
  • Based on using
actions: {
  increment ({ commit }) {
    commit('increment')}}Copy the code
  • Distribution of the Action
    store.dispatch('increment') // Distribute store.dispatch as a payload ('incrementAsync', {amount: 10}) // Distribute store.dispatch as an object ({amount: 10})type: 'incrementAsync',
      amount: 10
    })
    Copy the code
  • Call the asynchronous API and distribute multiple mutations
    actions: {commit ({commit, state}, products) {const savedCartItems = [...state.cart.added] Commit (types.checkout_request) // The shopping API accepts a success callback and a failure callback shop.buyProducts(products, Commit (types.checkout_success) => commit(types.checkout_failure, savedCartItems))}}Copy the code
  • Distribute the Action in the component
    • Similar to mutations, the action is distributed using this.$store.Dispatch (‘ XXX ‘)
    • Map the component’s methods to store.dispatch calls using the mapActions helper function
    methods: { ... mapActions(['increment', // maps' this.increment() 'to this.$store.dispatch('increment') // mapActions also supports loads:'incrementBy'// Map 'this.incrementBy(amount)' to this.$store.dispatch('incrementBy', amount) ]), ... mapActions({ add:'increment'// Map 'this.add()' to this.$store.dispatch('increment')})}Copy the code
  • Combination of the Action
// With async/await, we can combine action as follows // assume that getData() and getOtherData() return Promise Actions: { async actionA ({ commit }) { commit('gotData', await getData())
  },
  async actionB ({ dispatch, commit }) {
    await dispatch('actionA') // Wait for actionA to complete commit('gotOtherData', await getOtherData())
  }
}
Copy the code