what is vuex-router-sync for?

Guihai picture Guihai · May 25, 2017 · Viewed 13.2k times · Source

As far as I know vuex-router-sync is just for synchronizing the route with the vuex store and the developer can access the route as follows:

store.state.route.path
store.state.route.params

However, I can also handle route by this.$route which is more concise.

When do I need to use the route in the store, and what is the scenario in which I need vuex-router-sync?

Answer

choasia picture choasia · Aug 8, 2017

Here's my two cents. You don't need to import vuex-router-sync if you cannot figure out its use case in your project, but you may want it when you are trying to use route object in your vuex's method (this.$route won't work well in vuex's realm).

I'd like to give an example here.
Suppose you want to show a message in one component. You want to display a message like Have a nice day, Jack in almost every page, except for the case that Welcome back, Jack should be displayed when the user's browsing top page.

You can easily achieve it with the help of vuex-router-sync.

const Top = {
  template: '<div>{{message}}</div>',
  computed: {
    message() {
      return this.$store.getters.getMessage;
    }
  },
};
const Bar = {
  template: '<div>{{message}}</div>',
  computed: {
    message() {
      return this.$store.getters.getMessage;
    }
  }
};

const routes = [{
    path: '/top',
    component: Top,
    name: 'top'
  },
  {
    path: '/bar',
    component: Bar,
    name: 'bar'
  },
];

const router = new VueRouter({
  routes
});

const store = new Vuex.Store({
  state: {
    username: 'Jack',
    phrases: ['Welcome back', 'Have a nice day'],
  },
  getters: {
    getMessage(state) {
      return state.route.name === 'top' ?
        `${state.phrases[0]}, ${state.username}` :
        `${state.phrases[1]}, ${state.username}`;
    },
  },
});

// sync store and router by using `vuex-router-sync`
sync(store, router);

const app = new Vue({
  router,
  store,
}).$mount('#app');












// vuex-router-sync source code pasted here because no proper cdn service found
function sync(store, router, options) {
  var moduleName = (options || {}).moduleName || 'route'

  store.registerModule(moduleName, {
    namespaced: true,
    state: cloneRoute(router.currentRoute),
    mutations: {
      'ROUTE_CHANGED': function(state, transition) {
        store.state[moduleName] = cloneRoute(transition.to, transition.from)
      }
    }
  })

  var isTimeTraveling = false
  var currentPath

  // sync router on store change
  store.watch(
    function(state) {
      return state[moduleName]
    },
    function(route) {
      if (route.fullPath === currentPath) {
        return
      }
      isTimeTraveling = true
      var methodToUse = currentPath == null ?
        'replace' :
        'push'
      currentPath = route.fullPath
      router[methodToUse](route)
    }, {
      sync: true
    }
  )

  // sync store on router navigation
  router.afterEach(function(to, from) {
    if (isTimeTraveling) {
      isTimeTraveling = false
      return
    }
    currentPath = to.fullPath
    store.commit(moduleName + '/ROUTE_CHANGED', {
      to: to,
      from: from
    })
  })
}

function cloneRoute(to, from) {
  var clone = {
    name: to.name,
    path: to.path,
    hash: to.hash,
    query: to.query,
    params: to.params,
    fullPath: to.fullPath,
    meta: to.meta
  }
  if (from) {
    clone.from = cloneRoute(from)
  }
  return Object.freeze(clone)
}
.router-link-active {
  color: red;
}
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
<script src="https://unpkg.com/vuex/dist/vuex.js"></script>

<div id="app">
  <p>
    <router-link to="/top">Go to Top</router-link>
    <router-link to="/bar">Go to Bar</router-link>
  </p>
  <router-view></router-view>
</div>

fiddle here

As you can see, the components are well decoupled from vuex and vue-router's logic.
This pattern sometimes works really effectively for the case that you're not concerned about the relationship between current route and the value returned from vuex's getter.