Vuex data flow management

Posted by Kev on Tue, 30 Nov 2021 04:23:29 +0100

Component communication

Parent child component value transfer

Pass data to subcomponents through props

Parent

<template>
  <div>
    <h1>Pass values from parent to child</h1>
    <child title="My journey with Vue"></child>
  </div>
</template>

<script>
import child from './Child'
export default {
  components: {
    child
  }
}
</script>

Child

<template>
  <div>
    <h1>Props Down Child</h1>
    <h2>{{ title }}</h2>
  </div>
</template>

<script>
export default {
  // props: ['title'],
  props: {
    title: String
  }
}
</script>

Child parent component value transfer

By listening for subcomponent events

Parent

<template>
  <div>
    <h1>Pass value from child to parent</h1>
    There is no need to change the text here
    <child :fontSize="hFontSize" @enlargeText="enlargeText"></child>
  </div>
</template>

<script>
import child from './Child'
export default {
  components: {
    child
  },
  data () {
    return {
      hFontSize: 1
    }
  },
  methods: {
    enlargeText (size) {
      this.hFontSize += size
    }
  }
}
</script>

Child

<template>
  <div>
    <h1 :style="{ fontSize: fontSize + 'em' }">Props Down Child</h1>
    <button @click="handler">Text increase</button>
  </div>
</template>

<script>
export default {
  props: {
    fontSize: Number
  },
  methods: {
    handler () {
      this.$emit('enlargeText', 0.1)
    }
  }
}
</script>

Value transfer of unrelated components

Via EventBus

EventBus is also called event bus. In Vue, you can use the concept of EventBus as a communication bridge, just like all components share the same event center. You can register with the center to send or receive events, so components can notify other components in parallel up and down, but it is too convenient. If you use it carelessly, it will cause a "disaster" that is difficult to maintain, Therefore, a more perfect Vuex is needed as the state management center to raise the concept of notification to the shared state level.

  • initialization

First, you need to create an event bus and export it so that other modules can use or listen to it. We can deal with it in two ways. First, create a new. js file, such as eventbus.js

import Vue from 'vue'
export default new Vue()

Another way, You can directly initialize the global EventBus in main.js in the project

  • Send event

Now there are two unrelated components 01 and 02. When the corresponding operation in component 01 triggers a value change, the numchange event is sent

<!-- 01 -->
<template>
  <div>
    <h1>Unrelated components-01</h1>
    <div class="number" @click="sub">-</div>
    <input type="text" style="width: 30px; text-align: center;" :value="value">
    <div class="number" @click="add">+</div>
  </div>
</template>

<script>
import bus from './eventbus'

export default {
  props: {
    num: Number
  },
  created () {
    this.value = this.num
  },
  methods: {
    sub () {
      if (this.value > 1) {
        this.value--
        bus.$emit('numchange', this.value)
      }
    },
    add () {
      this.value++
      bus.$emit('numchange', this.value)
    }
  }
}
</script>

<style>
.number {
  display: inline-block;
  cursor: pointer;
  width: 20px;
  text-align: center;
}
</style>
  • Receive event
<!--02-->
<template>
  <div>
    <h1>Unrelated components-02</h1>

    <div>{{ msg }}</div>
  </div>
</template>

<script>
import bus from './eventbus'
export default {
  data () {
    return {
      msg: ''
    }
  },
  created () {
    bus.$on('numchange', (value) => {
      this.msg = `You have chosen ${value}Pieces of goods`
    })
  }
}
</script>

Other means of communication

Operate subcomponents through ref

Parent

<template>
  <div>
    <h1>ref Parent</h1>
    <child ref="c"></child>
  </div>
</template>

<script>
import child from './Child'
export default {
  components: {
    child
  },
  mounted () {
    this.$refs.c.test()
    this.$refs.c.value = 'hello input'
  }
}
</script>

Child

<template>
  <div>
    <h1>ref Child</h1>
    <input ref="input" type="text" v-model="value">
  </div>
</template>

<script>
export default {
  data () {
    return {
      value: ''
    }
  },
  methods: {
    test () {
      this.$refs.input.focus()
    }
  }
}
</script>

Vuex

What is Vuex?

State

Vuex uses a single state tree and contains all application level states with one object.

Use mapState to simplify the use of State in view, and mapState returns calculated properties

  • Array parameters
// This method is provided by vuex, so you should import it before using it
import { mapState } from 'vuex'
// mapState returns a calculated property named count and msg
// Use count and msg directly in the template
computed: {
  ...mapState(['count', 'msg']),
}
  • Object parameters
// This method is provided by vuex, so you should import it before using it
import { mapState } from 'vuex'
// By passing in an object, you can rename the returned calculated property
// Use num and message directly in the template
computed: {
  ...mapState({
    num: state => state.count,
    message: state => state.msg
  })
}

Getter

Getter is the calculation property in the store. mapGetter is used to simplify the use in the view

import { mapGetter } from 'vuex'

computed: {
  ...mapGetter(['reverseMsg']),
  // Rename and use reverse in the template
  ...mapGetter({ reverse: 'reverseMsg' })
}

Mutation

The only way to change the state in Vuex's store is to submit the mutation. The mutation in Vuex is very similar to the event: every
Each mutation has a string event type and a callback function (handler). This callback function is us
Where the state change is actually made, and it accepts state as the first parameter

The advantage of using Mutation to change the state is that the state changes can be tracked no matter where they are modified
Modification of status. It can realize advanced time travel debugging function

import { mapMutations } from 'vuex'
methods: {
  ...mapMutations(['increate']),
  // Pass object to solve the problem of duplicate name
 ...mapMutations({ increateMut: 'increate' })
}

Action

Action is similar to mutation in that:

  • The Action submits the mutation instead of directly changing the status.
  • An Action can contain any asynchronous Action.
import { mapActions } from 'vuex'
methods: {
  ...mapActions(['increate']),
  // Pass object to solve the problem of duplicate name
  ...mapActions({ increateAction: 'increate' })
 }

Module

Due to the use of a single state tree, all the states of the application will be concentrated in a relatively large object. When the application becomes very complex, the store pair
Elephants can become quite bloated.

To solve the above problems, Vuex allows us to divide the store into modules. Each module has its own state
mutation, action, getter, and even nested sub modules

Vuex plug-in

Vuex's store accepts the plugins option, which exposes the hook of each mutation. The vuex plug-in is a function that receives store as a unique parameter:

It is not allowed to modify the state directly in a plug-in -- similar to a component, the change can only be triggered by submitting a mutation.

const myPlugin = store => {
  // When store is initialized, it is called.
  store.subscribe((mutation, state) => {
    // After each mutation, the call is made.
    // The format of mutation is {type, payload}
  })
}

// use
const store = new Vuex.Store({
  // ...
  plugins: [myPlugin]
})

Strict mode

In strict mode, whenever a state change occurs and is not caused by the mutation function, an error will be thrown. This ensures that all state changes can be tracked by the debugging tool.

const store = new Vuex.Store({
  // ...
  strict: true
})

Simulation Implementation

  • Implement the install method

    • Vuex is a Vue plug-in. First implement the install method agreed by the Vue plug-in
  • Implement Store class

    • Implement the constructor and receive options
    • Responsive processing of state
    • Implementation of getter
    • commit and dispatch methods

install method

function install(Vue) {
  _Vue = Vue;
  _Vue.mixin({
    beforeCreate() {
      if (this.$options.store) {
        _Vue.prototype.$store = this.$options.store;
      }
    },
  });
}

Store class

class Store {
  constructor(options) {
    const { state = {}, getters = {}, mutations = {}, actions = {} } = options;
    this.state = _Vue.observable(state);
    this.getters = Object.create(null);
    Object.keys(getters).forEach((key) => {
      Object.defineProperty(this.getters, key, {
        get: () => getters[key](state),
      });
    });
    this._mutations = mutations;
    this._actions = actions;
  }

  commit(type, payload) {
    this._mutations[type](this.state, payload);
  }

  dispatch(type, payload) {
    this._actions[type](this, payload);
  }
}

Topics: Vue.js