skip to Main Content

I have a Vue 3 app with vue router. I have a page where I make an API call on mounted() that return me some data in an array (like 100 elements), then I have a vue components that render every elements fetched from the API. So my page list all elements and user can scroll the page. Everthing is fine.

Then I click on a element (for example the 70th) and the single page is loaded (as excepted).

Now I want to go back, so I click on my browser back button and it reload my page where all elements are listed but since the elements are not fetched from the API the page has no scroll and it put me a the top of the page, few seconds later the elements are fetched and display on my page.

I want when I go back to be at the correct position (so in my example at the 70th elements) and not at the top of the page. Is there a way to force Vue to fetch data before rendering the page ?

 export default {
    data() {
        return {
            elements: {},
        }
    },
    mounted(){           
        const route = useRoute()
        this.params = route.params

        axios.get('/elements')
        .then((response) => {
            this.elements = response.data.elements
        })
    },
}

2

Answers


  1. Chosen as BEST ANSWER

    So my problem was that in my vue router, I had the scroll behavior set to scroll at the top of the page. Based on Vue Router documentation, I adapted it to fix my problem.

    const router = createRouter({
        scrollBehavior(to, from, savedPosition) {
            if (savedPosition) {
                return savedPosition
             } else {
                return { top: 0 }
             }
        },
    })
    

  2. First wrap your page component with <KeepAlive>, this will cache the page component instance when navigating.

    <!-- App.vue -->
    <template>
      <RouterView v-slot="{ Component }">
        <KeepAlive>
          <component :is="Component" />
        </KeepAlive>
      </RouterView>
    </template>
    

    Then replace onMounted with onBeforeMount. <KeepAlive> will restore component state and trigger only onActivated and onMounted, so elements are still there and fetchElements won’t be called again.

    <script lang="ts" setup>
    import { ref, onBeforeMount } from 'vue';
    import { fetchElements } from '../api';
    
    const elements = ref<string[]>([]);
    
    onBeforeMount(async () => {
      elements.value = (await fetchElements()).data.elements;
    });
    </script>
    
    <template>
      <ol>
        <li v-for="(item, index) in elements" :key="index">
          {{ item }}
        </li>
      </ol>
      <RouterLink to="/">Go to /</RouterLink>
    </template>
    

    Done. demo


    If your project is large, using <KeepAlive> on all routes might lead to performance issues. Use include to only cache the components that need to be cached.

    <KeepAlive include="List">
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search