27

I would like to use the same component for different routes in a Vue.js application.

I currently have something like this:


main.js

const routes = [
    { path: '/route-1', name: 'route-1', component: MyComponent },
    { path: '/route-2', name: 'route-2', component: MyComponent },
    { path: '/route-3', name: 'route-3', component: MyComponent },

]

const router = new VueRouter({
    routes
})

myComponent.vue

<ul>
    <li><router-link to="/route-1">Route 1</router-link></li>
    <li><router-link to="/route-2">Route 2</router-link></li>
    <li><router-link to="/route-3">Route 3</router-link></li>
</ul>

When I type the route manually in the browser, everything is working well, but when I try to navigate between the routes using some of these router-generated-links, nothing happens. The route changes but the content is still the same. Any idea how I can solve this?

Thanks!

Abhishek
  • 1,477
  • 1
  • 10
  • 18
Moisés Pio
  • 393
  • 1
  • 4
  • 11

3 Answers3

100

This is expected behaviour as Vue is trying to be optimal and reuse existing components. The behaviour you want to achieve used to be solved with a setting called canReuse, but that has been deprecated. The current recommended solution is to set a unique :key property on your <router-view> like so:

<router-view :key="$route.path"></router-view>

Check out this JSFiddle example.

mzgajner
  • 2,250
  • 1
  • 17
  • 14
  • 5
    Works like a charm! I understand the reasons why that's the expected behaviour, but since I have three similar pages with just some content differences, I have to use three different routes to load the right content for each page. That's my very first project and maybe I will find a more elegant solution to achieve the same result. But yeah, thank you! :) – Moisés Pio Mar 06 '17 at 05:58
  • 4
    Duuuuuuddddeeeee I was going CRAZY. Glad I found this answer – ihor.eth Nov 06 '20 at 04:10
11

You can use watch property, so your component will not waste time to reloading:

index.js You might have something like this

const routes = [
  {
    path: '/users/:id',
    component: Vue.component('user', require('./comp/user.vue').default)
  }
]

user.vue

created(){
  // will fire on component first init
  this.init_component();
},
watch: {
  // will fire on route changes
//'$route.params.id': function(val, oldVal){ // Same
  '$route.path': function(val, oldVal){
    console.log(this.$route.params.id);
    this.init_component();
  }
},
methods: {
  init_component: function(){
    // do anything you need
    this.load_user_data_with_ajax();
  },
}
Serg_x
  • 320
  • 3
  • 7
2

Just to make a note. If anybody is working with SSR template, things are a bit different. @mzgajner's answer does indeed recreate the component but will not trigger the asyncData again.

For that to happen, modify entry-client.js like this.

OLD:

const activated = matched.filter((c, i) => {
    return diffed || (diffed = (prevMatched[i] !== c))
})

NEW:

const activated = matched.filter((c, i) => {

    /*
        In my case I only needed this for 1 component
    */

    diffed = ((prevMatched[i] !== c) || c.name == 'p-page-property-map')

    return diffed
})
Borjante
  • 9,767
  • 6
  • 35
  • 61