FrontendDeveloper.in

Vue.js Interview Questions

Filter by topic:
  • Vuex provides actions property similar mutations property in order to define action handlers. These action handlers receive context object as an argument which has same properties and methods of store instance.

    Let's see counter example to demonstrate increment action which commits respective mutation,

    const store = new Vuex.Store({
    state: {
    count: 0
    },
    mutations: {
    increment (state) {
    state.count++
    }
    },
    actions: {
    increment (context) {
    context.commit('increment')
    }
    }
    })
    
  • Styled components is a CSS-in-JS library used mainly for ReactJS applications. If you want to use it for VueJS applications, there is a VueJS styled components library port available, however, it is not a common practice.

  • You can dispatch actions in components with this.$store.dispatch('action name'), or use the mapActions helper which maps component methods to store.dispatch calls.

    For example, you can dispatch increment actions in counter component as below,

    import { mapActions } from 'vuex'
    
    export default {
    // ...
    methods: {
    ...mapActions([
    'increment', // map `this.increment()` to `this.$store.dispatch('increment')`
    
    // `mapActions` also supports payloads:
    'incrementBy' // map `this.incrementBy(amount)` to `this.$store.dispatch('incrementBy', amount)`
    ]),
    ...mapActions({
    add: 'increment' // map `this.add()` to `this.$store.dispatch('increment')`
    })
    }
    }
    
  • You can write multiple actions together to handle more complex async flows either by chaining promises or async/await. i.e, store.dispatch can handle Promise returned by the triggered action handler and it also returns Promise.

    Let's take two actions to see how they are combined and handled async flows,

    actions: {
    actionOne ({ commit }) {
    return new Promise((resolve, reject) => {
    setTimeout(() => {
    commit('first mutation')
    resolve()
    }, 1000)
    })
    },
    actionTwo ({ dispatch, commit }) {
    return dispatch('actionA').then(() => {
    commit('second mutation')
    })
    }
    }
    

    As per the above example, When you try to dispatch actionTwo it dispatches actionOne first and then commits respective mutation. You can still simplify with async/await as below,

    actions: {
    async actionOne ({ commit }) {
    commit('first mutation', await getDataAsPromise())
    },
    async actionTwo ({ dispatch, commit }) {
    await dispatch('actionOne') // wait for `actionA` to finish
    commit('second mutation', await getSomeDataAsPromise())
    }
    }
    
  • If you keep all state of our application in a single big state, the store can get really bloated. To solve this problem, Vuex allows us to divide our store into modules. Here, each module can contain its own state, mutations, actions, getters, and even nested modules.

    Let's take an example with multiple modules, configuring them in vuex and accessing different modules,

    const moduleOne = {
    state: { ... },
    mutations: { ... },
    actions: { ... },
    getters: { ... }
    }
    
    const moduleTwo = {
    state: { ... },
    mutations: { ... },
    actions: { ... },
    getters: { ... }
    }
    
    const store = new Vuex.Store({
    modules: {
    one: moduleOne,
    two: moduleTwo
    }
    })
    
    store.state.one // -> `moduleOne's state
    store.state.two // -> `moduleTwo's state
    
  • When you use modules the local state will be available to mutations, getters and actions in different ways.

    1. Both mutations and getters will receive module local state as first argument.
    const moduleOne = {
    state: { count: 0 },
    mutations: {
    increment (state) {
    state.count++; // Here state refers local module state
    }
    },
    
    getters: {
    average (state) {
    return state.count / 2
    }
    }
    }
    
    1. In actions, local state will be available as first argument.
    const moduleOne = {
    actions: {
    incrementConditional ({ state, commit, rootState }) {
    if (state.count < rootState.count) {
    commit('increment')
    }
    }
    }
    }
    
  • Sometime you may need to create multiple instances of a module.

    For example, it is needed in the below cases,

    1. If multiple stores that use the same module
    2. Register the same module multiple times in the same store.

    In those cases, you need to assign to a variable and export it for reusability,

    const MyReusableModule = {
    // state
    // mutations, actions, getters...
    }
    
  • Vuex enforces below high-level principles,

    1. The Application-level state need to be centralized in the store
    2. The state should be mutated by committing mutations only(i.e, for synchronous transactions)
    3. The actions should be used for asynchronous transactions.
  • In strict mode, you can't mutate state directly using v-model attribute. If you use v-model it throws an error because mutation is not performed inside an explicit Vuex mutation handler.

    For example, the below input throws an error due to v-model usage

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

    In this case, you need to bind the <input>'s value. It can be resolved using value attribute as below,

    <input :value="username" @input="updateProfile">
    
    computed: {
    ...mapState({
    username: state => state.user.username
    })
    },
    methods: {
    updateProfile (e) {
    this.$store.commit('updateProfile', e.target.value)
    }
    },
    mutations: {
    updateProfile (state, username) {
    state.user.username = username
    }
    }
    
  • You can still use model directive using two-way computed property with a setter.

    <input v-model="username">
    computed: {
     username: {
    get () {
    return this.$store.state.user.username
    },
    set (value) {
    this.$store.commit('updateProfile', value)
    }
     }
    }
    mutations: {
    updateProfile (state, username) {
    state.user.username = username
    }
    }