如何在Vuex actions中使用Vue Router进行导航

102

我正在使用Vue 2.x和Vuex 2.x创建一个Web应用程序。 我通过http调用从远程位置获取一些信息,如果该调用失败,我希望重定向到其他页面。

GET_PETS: (state) => {
  return $http.get('pets/').then((response)=>{
      state.commit('SET_PETS', response.data)
    })
  },
  error => {this.$router.push({path:"/"}) }
  )
}

但是this.$router.push({path:"/"})给我以下错误:

Uncaught (in promise) TypeError: Cannot read property 'push' of undefined

如何实现此操作。

模拟的 JsFiddle: 这里


这是因为您将其用于将“this”视为文字的箭头函数中。要么将其转换为常规函数,要么使用下面建议的答案... - EldadT
6个回答

230
只要你在应用程序的某个地方导出了你的路由实例(例如./router.js),你就可以在任何你想要的地方使用import,包括在Vuex存储中使用它。
import router from './router';

从那里开始,你可以使用router.push()
就这么简单。

2
无法在Quasar v2上运行模块未找到:无法解析导入的依赖项“./router” - eeerrrttt
非常直接的答案。谢谢! - Madian Malfi

22

这个例子可能对你有所帮助。

main.js

import Vue from "vue";
import VueRouter from "vue-router";

...

Vue.use(VueRouter);

export const router = new VueRouter({
    mode: 'hash',
    base: "./",
    routes: [
        { path: "/", component: welcome},
        { path: "/welcome", component: welcome},

    ]
})

actions.js

import {router} from "../main.js"

export const someAction = ({commit}) => {

    router.push("/welcome");
} 

3
这不是你应该使用Vue的方式。操作不应该有副作用。在组件的方法中解决这个问题,例如 dispatch('someAction').then(() => this.$router.push('/welcome')) - adriaan
5
@adriaan,这并不完全正确,即使在vuex演示中的操作也会产生副作用。 - Blowsie
6
事实上,动作可以有许多副作用,这正是它们处理非确定性和异步逻辑的目的。而变异是那些应该严格没有副作用的操作。 - Tom 'Blue' Piddock
1
我猜我的评论就无效了。谢谢你澄清这一点! - adriaan
@adriaan 你在考虑变异和获取器。 - user9993
我也这么认为。我来自Ember.js,我想我还没有在脑海中更新我的文档 :) - adriaan

12

初始回答

main.js 中(即我们“安装”所有模块并创建Vue实例的文件,即src/main.js):

const vm = new Vue({
  el: '#app',
  router,
  store,
  apolloProvider,
  components: { App },
  template: '<App/>'
})

export { vm }

这是我的例子,但在我们的情况下,最重要的是const vmrouter

在你的store中:

import { vm } from '@/main'

yourMutation (state, someRouteName) {
  vm.$router.push({name: someRouteName})
}

P.S. 通过使用import { vm } from '@/main',我们可以访问在Vuex中所需的任何内容,例如bootstrap-vue某些组件需要的vm.$root

P.P.S. 看起来只有当所有内容都加载完成后,我们才能使用vm。换句话说,如果我们在mounted()中调用someMutation,则不能在其中使用vm,因为mounted()会在vm创建之前出现/发生。


新答案:

Constantin的答案(被接受的那个)比我的好,所以我想为新手展示如何实现它。

在core目录中(在我这里是/src中),与App.vuemain.js和其他文件并列,我有一个名为router.js的文件,其内容如下:

import Vue from 'vue'
import Router from 'vue-router'

// Traditional loading
import Home from '@/components/pages/Home/TheHome'

// Lazy loading (lazy-loaded when the route is visited)
const Page404 = () => import(/* webpackChunkName: "Page404" */ '@/components/pages/404)
const Page503 = () => import(/* webpackChunkName: "Page503" */ '@/components/pages/503)

Vue.use(Router)

const router = new Router({
  mode: 'hash',
  base: process.env.BASE_URL,
  linkExactActiveClass: 'active',
  routes: [
    {
      path: '*',
      name: 'Page404',
      component: Page404
    },

    {
      path: '*',
      name: 'Page503',
      component: Page503
    },

    {
      path: '/',
      name: 'Home',
      component: Home
    },

    // Other routes
    {....},
    {....}
  ]
})

// Global place, if you need do anything before you enter to a new route.
router.beforeEach(async (to, from, next) => {
  next()
})

export default router

将我们的路由器导入到 main.js 中:

import Vue from 'vue'
import App from './App.vue'
import router from './router'

Vue.config.productionTip = false

const vm = new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app')

export { vm }

最后,在您的组件,Vuex或任何其他地方,import router from './router'并进行您需要的任何操作,例如router.push(...)


10

看起来你没有将路由器注入到你的应用程序中,因此它是“未定义的”

在以前的vue-router版本中,你需要使用Vue.use(VueRouter),而在2.0中,你可以像下面这样将路由器注入到应用程序中:

const routes = [
  { path: '/foo', component: Foo },
]

const router = new VueRouter({
  routes
})

const app = new Vue({
  router // inject the router
}).$mount('#app')

这样做将使其作为this.$router在整个应用程序中可用。
以下是相关问题的回答:如何从Vuex状态中使用Vue Router?,似乎Vuex不会在this.$router处接收到路由器实例。因此,建议采用两种方法来提供对路由器实例的访问。
第一种方法更为直接,涉及将webpack全局设置为该实例。
第二种方法涉及使用Promises和您的vuex操作,这将允许您的组件在操作Promise解析/拒绝后利用其对路由器实例的引用。

2
这应该是针对Vue 2的具体答案。 - codely

7

我不喜欢在Store中将应用程序位置状态与其他状态分开管理,并且需要同时管理路由和Store,因此我创建了一个Vuex模块,该模块在Store内部管理位置状态。

现在我可以通过调度操作来进行导航,就像任何其他状态更改一样:

dispatch("router/push", {path: "/error"})

这样做的好处是使像动画页面转换这样的事情更容易处理。

自己编写路由器模块并不难,但如果您愿意,也可以尝试使用我的:

https://github.com/geekytime/vuex-router


4
你可以像这样从路由器目录中导入路由:

import route from router directory

import router from '@/router'

router.push({name: 'Home'})

这个 @ 符号代替了路径到 src 目录的位置


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接