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?
Vuex is a state management pattern + library for Vue. js applications. It serves as a centralized store for all the components in an application, with rules ensuring that the state can only be mutated in a predictable fashion.
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>
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.
I saw this thread when I was learning Vue. Added some of my understanding on the question.
Vuex defines a state management pattern for Vue applications. Instead of defining component props and passing the shared state through props in all the places, we use a centralized store to organize the state shared by multiple components. The restriction on state mutation makes the state transition clearer and easier to reason about.
Ideally, we should get / build consistent (or identical) views if the provided store states are the same. However, the router, shared by multiple components, breaks this. If we need to reason about why the page is rendered like it is, we need to check the store state as well as the router state if we derive the view from the this.$router
properties.
vuex-router-sync
is a helper to sync the router state to the centralized state store. Now all the views can be built from the state store and we don't need to check this.$router
.
Note that the route
state is immutable, and we should "change" its state via the $router.push
or $router.go
call. It may be helpful to define some actions on store as:
// import your router definition
import router from './router'
export default new Vuex.Store({
//...
actions: {
//...
// actions to update route asynchronously
routerPush (_, arg) {
router.push(arg)
},
routerGo (_, arg) {
router.go(arg)
}
}
})
This wraps the route
updates in the store actions and we can completely get rid of the this.$router
dependencies in the components.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With