如何从 Vuex 状态使用 Vue Router?
How to use Vue Router from Vuex state?
在我的组件中,我一直在使用:
this.$router.push({ name: 'home', params: { id: this.searchText }});
改变路线。我现在已经将一个方法移动到我的 Vuex 操作中,当然 this.$router
不再有效。 Vue.router
也没有。那么,请问如何从 Vuex 状态调用路由器方法?
我假设 vuex-router-sync 在这里无济于事,因为您需要路由器实例。
因此,尽管这感觉不太理想,但您可以将实例设置为 webpack 中的全局实例,即
global.router = new VueRouter({
routes
})
const app = new Vue({
router
...
现在您应该能够:router.push({ name: 'home', params: { id: 1234 }})
从应用程序中的任何地方
作为替代方案,如果您不喜欢上述想法,您可以 return 从您的行动中获得承诺。然后,如果操作成功完成,我假设它调用了突变或其他东西,你可以 resolve
Promise。但是,如果它失败了,无论重定向需要什么条件,你都会 reject
Promise。
通过这种方式,您可以将路由器重定向移动到一个组件中,该组件只需捕获被拒绝的 Promise 并触发 vue-router 推送,即
# vuex
actions: {
foo: ({ commit }, payload) =>
new Promise((resolve, reject) => {
if (payload.title) {
commit('updateTitle', payload.title)
resolve()
} else {
reject()
}
})
# component
methods: {
updateFoo () {
this.$store.dispatch('foo', {})
.then(response => { // success })
.catch(response => {
// fail
this.$router.push({ name: 'home', params: { id: 1234 }})
})
我相信 rootState.router
将在您的操作中可用,假设您在主 Vue 构造函数中将 router
作为选项传递。
正如 GuyC 所提到的,我还认为你最好在它解析后从你的操作和路由中返回一个承诺。简单来说:dispatch(YOUR_ACTION).then(router.push())
.
我的情况是,我发现自己使用 .go
而不是 .push
。
抱歉,没有解释原因,但就我而言它有效。我把这个留给像我这样的未来 Google 员工。
state: {
anyObj: {}, // Just filler
_router: null // place holder for router ref
},
mutations: {
/***
* All stores that have this mutation will run it
*
* You can call this in App mount, eg...
* mounted () {
* let vm = this
* vm.$store.commit('setRouter', vm.$router)
* }
*
setRouter (state, routerRef) {
state._router = routerRef
}
},
actions: {
/***
* You can then use the router like this
* ---
someAction ({ state }) {
if (state._router) {
state._router.push('/somewhere_else')
} else {
console.log('You forgot to set the router silly')
}
}
}
}
更新
在我发布这个答案后,我注意到按照我呈现 Typescript 的方式定义它停止检测 state
的字段。我认为那是因为我使用 any
作为类型。我可能可以手动定义类型,但这听起来像是在对我重复你自己。这就是现在我最终得到一个函数而不是扩展 class 的方式(如果有人知道,我会很高兴让我知道其他解决方案)。
import { Store } from 'vuex'
import VueRouter from 'vue-router'
// ...
export default (router: VueRouter) => {
return new Store({
// router = Vue.observable(router) // You can either do that...
super({
state: {
// router // ... or add `router` to `store` if You need it to be reactive.
// ...
},
// ...
})
}
import Vue from 'vue'
import App from './App.vue'
import createStore from './store'
// ...
new Vue({
router,
store: createStore(router),
render: createElement => createElement(App)
}).$mount('#app')
初步回答内容
我个人只是为典型的 Store
class.
做了一个包装
import { Store } from 'vuex'
import VueRouter from 'vue-router'
// ...
export default class extends Store<any> {
constructor (router: VueRouter) {
// router = Vue.observable(router) // You can either do that...
super({
state: {
// router // ... or add `router` to `store` if You need it to be reactive.
// ...
},
// ...
})
}
}
如果您需要$route
,您可以使用router.currentRoute
。请记住,如果您希望 getters
和 router.currentRoute
按预期工作,您宁愿需要 router
反应。
在“main.ts”(或“.js”)中,我只是将它与 new Store(router)
一起使用。
import Vue from 'vue'
import App from './App.vue'
import Store from './store'
// ...
new Vue({
router,
store: new Store(router),
render: createElement => createElement(App)
}).$mount('#app')
在我的组件中,我一直在使用:
this.$router.push({ name: 'home', params: { id: this.searchText }});
改变路线。我现在已经将一个方法移动到我的 Vuex 操作中,当然 this.$router
不再有效。 Vue.router
也没有。那么,请问如何从 Vuex 状态调用路由器方法?
我假设 vuex-router-sync 在这里无济于事,因为您需要路由器实例。
因此,尽管这感觉不太理想,但您可以将实例设置为 webpack 中的全局实例,即
global.router = new VueRouter({
routes
})
const app = new Vue({
router
...
现在您应该能够:router.push({ name: 'home', params: { id: 1234 }})
从应用程序中的任何地方
作为替代方案,如果您不喜欢上述想法,您可以 return 从您的行动中获得承诺。然后,如果操作成功完成,我假设它调用了突变或其他东西,你可以 resolve
Promise。但是,如果它失败了,无论重定向需要什么条件,你都会 reject
Promise。
通过这种方式,您可以将路由器重定向移动到一个组件中,该组件只需捕获被拒绝的 Promise 并触发 vue-router 推送,即
# vuex
actions: {
foo: ({ commit }, payload) =>
new Promise((resolve, reject) => {
if (payload.title) {
commit('updateTitle', payload.title)
resolve()
} else {
reject()
}
})
# component
methods: {
updateFoo () {
this.$store.dispatch('foo', {})
.then(response => { // success })
.catch(response => {
// fail
this.$router.push({ name: 'home', params: { id: 1234 }})
})
我相信 rootState.router
将在您的操作中可用,假设您在主 Vue 构造函数中将 router
作为选项传递。
正如 GuyC 所提到的,我还认为你最好在它解析后从你的操作和路由中返回一个承诺。简单来说:dispatch(YOUR_ACTION).then(router.push())
.
我的情况是,我发现自己使用 .go
而不是 .push
。
抱歉,没有解释原因,但就我而言它有效。我把这个留给像我这样的未来 Google 员工。
state: {
anyObj: {}, // Just filler
_router: null // place holder for router ref
},
mutations: {
/***
* All stores that have this mutation will run it
*
* You can call this in App mount, eg...
* mounted () {
* let vm = this
* vm.$store.commit('setRouter', vm.$router)
* }
*
setRouter (state, routerRef) {
state._router = routerRef
}
},
actions: {
/***
* You can then use the router like this
* ---
someAction ({ state }) {
if (state._router) {
state._router.push('/somewhere_else')
} else {
console.log('You forgot to set the router silly')
}
}
}
}
更新
在我发布这个答案后,我注意到按照我呈现 Typescript 的方式定义它停止检测 state
的字段。我认为那是因为我使用 any
作为类型。我可能可以手动定义类型,但这听起来像是在对我重复你自己。这就是现在我最终得到一个函数而不是扩展 class 的方式(如果有人知道,我会很高兴让我知道其他解决方案)。
import { Store } from 'vuex'
import VueRouter from 'vue-router'
// ...
export default (router: VueRouter) => {
return new Store({
// router = Vue.observable(router) // You can either do that...
super({
state: {
// router // ... or add `router` to `store` if You need it to be reactive.
// ...
},
// ...
})
}
import Vue from 'vue'
import App from './App.vue'
import createStore from './store'
// ...
new Vue({
router,
store: createStore(router),
render: createElement => createElement(App)
}).$mount('#app')
初步回答内容
我个人只是为典型的 Store
class.
import { Store } from 'vuex'
import VueRouter from 'vue-router'
// ...
export default class extends Store<any> {
constructor (router: VueRouter) {
// router = Vue.observable(router) // You can either do that...
super({
state: {
// router // ... or add `router` to `store` if You need it to be reactive.
// ...
},
// ...
})
}
}
如果您需要$route
,您可以使用router.currentRoute
。请记住,如果您希望 getters
和 router.currentRoute
按预期工作,您宁愿需要 router
反应。
在“main.ts”(或“.js”)中,我只是将它与 new Store(router)
一起使用。
import Vue from 'vue'
import App from './App.vue'
import Store from './store'
// ...
new Vue({
router,
store: new Store(router),
render: createElement => createElement(App)
}).$mount('#app')