Search code examples
vue.jsvuejs3vue-composition-api

How to reduce boilerplate code in Vue 3 Composition API (eg router, store)


With Vue 3 Composition API, each view needs to have these:

import { useRouter, useRoute } from 'vue-router'
import { useStore } from 'vuex'


export default {
    setup() {
        const router = useRouter()
        const store = useStore()

        // ...
     }
}

Is there a way to declare these somehow just once, pass them to the created app, and simply use them without these declarations inside the app views? With vue2, these were passed when app was initialized, then this.$store / this.$router simply worked.

An idea with global vars, which can easily cause issues: in app.vue, these could be declared once like this:

import { useStore } from 'vuex'

export default {
    setup() {
        globalthis.store = useStore()

then store would work everywhere.


Solution

  • The component instance is not available in setup() because the component has not been created yet, so there's no this context in the Composition API to use this.$store.

    I believe the only way to make the store/router variables available in setup() without any other imports is to attach them to window/globalThis as globals (ignoring the caveats of globals):

    // router.js
    import { createRouter } from 'vue-router'
    export default createRouter({/*...*/})
    
    // store.js
    import { createStore } from 'vuex'
    export default createStore({/*...*/})
    
    // main.js
    import router from './router'
    import store from './store'
    
    window.$router = router
    window.$store = store
    

    Note the store and router are still available in the Options API and within the template as $store and $router, respectively, with Vuex 4 and Vue Router 4:

    <template>
      <div>{{ $store.state.myProp }}</div>
      <button @click="$router.back()">Back</button>
    </template>
    
    <script>
    export default {
      mounted() {
        console.log(this.$store.state.myProp)
        console.log(this.$router.currentRoute)
      }
    }
    </script>