Application Structure

Vuex doesn't really restrict how you structure your code. Rather, it enforces a set of high-level principles:

As long as you follow these rules, it's up to you how to structure your project. If your store file gets too big, simply start splitting the actions, mutations and getters into separate files.

For any non-trivial app, we will likely need to leverage modules. Here's an example project structure:

Strict Mode

To enable strict mode, simply pass in strict: true when creating a Vuex store:

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

In strict mode, whenever Vuex state is mutated outside of mutation handlers, an error will be thrown. This ensures that all state mutations can be explicitly tracked by debugging tools.

Development vs. Production

Do not enable strict mode when deploying for production! Strict mode runs a synchronous deep watcher on the state tree for detecting inappropriate mutations, and it can be quite expensive when you make large amount of mutations to the state. Make sure to turn it off in production to avoid the performance cost.

Similar to plugins, we can let the build tools handle that:

const store = new Vuex.Store({
  // ...
  strict: process.env.NODE_ENV !== 'production'
})

The above code dynamically detects if the environment is production. If it is, the strict mode is disabled. Otherwise it is enabled. Btw, what is process here?

Form Handling

When using Vuex in strict mode, it could be a bit tricky to use v-model on a piece of state that belongs to Vuex:

<input v-model="obj.message">

Assuming obj is a computed property that returns an Object from the store, the v-model here will attempt to directly mutate obj.message when the user types in the input. In strict mode, this will result in an error because the mutation is performed directly without commiting mutation.

To deal with the above problem you can use the following approach -

<input :value="message" @input="updateMessage">

// ...
computed: {
  ...mapState({
    message: state => state.obj.message
  })
},
methods: {
  updateMessage (e) {
    this.$store.commit('updateMessage', e.target.value)
  }
}


// Mutation Handler --
mutations: {
  updateMessage (state, message) {
    state.obj.message = message
  }
}

The above approach uses v-bind instead of v-model. And Whenever we type something, the updateMessage is called which mutates the obj object.

But the above approach is little bit verbose and if there's many inputs, the code gets messier. Also, we will loose many feature from v-model. There is another alternative way to solve this problem using setter on computed property.

Two-way Computed Property

<input v-model="message">
// ...
computed: {
  message: {
    get () {
      return this.$store.state.obj.message
    },
    set (value) {
      this.$store.commit('updateMessage', value)
    }
  }
}