Vue and Vuex

Vuex

The workflow of store workflow

Good example

Basic Concepts

Store is the center of vuex. Store is where vuex saves all data together. Store will hold the application State.
When Vue components retrieve state from it, they will reactively and efficiently update if the store’s state changes.

const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment (state) {
      state.count++
    }
  }
})

You cannot directly mutate the store’s state. The only way to change a store’s state is by explicitly committing mutations.

If we want to execute the increment function,

store.commit('increment')

Vuex uses a single state tree which contains all your application level state and serves as the “single source of truth”.

This also means usually you will have only one store for each application

For the best practice, we can create a store folder.

store
  |_index.js
  |_action.js
  |_mutation.js
  |_getter.js
  |_mutation_types.js

Within the store/index.js, we set:

Vue.use(Vuex)

Then use it in main.js

new Vue({
	router,
	store,
}).$mount('#app')

By providing the store option to the root instance, the store will be injected into all child components of the root and will be available on them as this.$store

For Example:

const Counter = {
  template: `<div>{{ count }}</div>`,
  computed: {
    count () {
      return this.$store.state.count
    }
  }
}

mapState

mapState is a helper for vuex. When using the state within the component, it will be insufficient to declare getter function one by one. We can use mapState to simply map one this attribute to state.

e.g.

// in full builds helpers are exposed as Vuex.mapState
import { mapState } from 'vuex'

export default {
  // ...
  computed: mapState({
    // arrow functions can make the code very succinct!
    count: state => state.count,

    // passing the string value 'count' is same as `state => state.count`
    countAlias: 'count',

    // to access local state with `this`, a normal function must be used
    countPlusLocalState (state) {
      return state.count + this.localCount
    }
  })
}

Object Spread Operator

it’s kind of advanced function for mapState e.g.

computed: {
  localComputed () { /* ... */ },
  // mix this into the outer object with the object spread operator
  ...mapState({
    // ...
  })
}

getters

getters is a list of function defined in Store which can be reused in multiple component.

mapGetters

mapGetters is similar to mapState. It reduce the complexity of importing getters function.

Commit with Payload

mutations: {
  increment (state, payload) {
    state.count += payload.amount
  }
}
store.commit('increment', {
  amount: 10
})

Using Constants for Mutation Types

The best practice is to define mutation types within the project.

// 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: {
    // we can use the ES2015 computed property name feature
    // to use a constant as the function name
    [SOME_MUTATION] (state) {
      // mutate state
    }
  }
})

Mutations Must Be Synchronous

But we can use async/await within the mutation functions

mapMutations

mapMutations is a better way to import mutation functions

import { mapMutations } from 'vuex'

export default {
  // ...
  methods: {
    ...mapMutations([
      'increment', // map this.increment() to this.$store.commit('increment')

      // mapMutations also supports payloads:
      'incrementBy' // this.incrementBy(amount) maps to this.$store.commit('incrementBy', amount)
    ]),
    ...mapMutations({
      add: 'increment' // map this.add() to this.$store.commit('increment')
    })
  }
}

Modules

Due to using a single state tree, all state of our application is contained inside one big object. However, as our application grows in scale, the store can get really bloated.

To help with that, Vuex allows us to divide our store into modules. Each module can contain its own state, mutations, actions, getters, and even nested modules – it’s fractal all the way down:

const moduleA = {
  state: { ... },
  mutations: { ... },
  actions: { ... },
  getters: { ... }
}

const moduleB = {
  state: { ... },
  mutations: { ... },
  actions: { ... }
}

const store = new Vuex.Store({
  modules: {
    a: moduleA,
    b: moduleB
  }
})

store.state.a // -> moduleA's state
store.state.b // -> moduleB's state

Action

Asynchronous logic should be encapsulated in, and can be composed with actions.

├── index.html
├── main.js
├── api
│   └── ... # abstractions for making API requests
├── components
│   ├── App.vue
│   └── ...
└── store
    ├── index.js          # where we assemble modules and export the store
    ├── actions.js        # root actions
    ├── mutations.js      # root mutations
    └── modules
        ├── cart.js       # cart module
        └── products.js   # products module

CSS Grid

CSS grid is a layput solution.

Link