仅在命中 Vue Router 子路由时触发 API 调用

Fire API call only when a Vue Router child route is hit

提问人:maxshuty 提问时间:11/13/2023 最后编辑:maxshuty 更新时间:11/15/2023 访问量:53

问:

我正在使用 Vue2 和 vue 路由器 3x。每当点击特定子路由时,我该如何执行存储操作,无论它是通过按钮按下还是浏览器后退/前进按钮点击?

每当命中特定路由时,我都需要触发 API 调用。目前代码工作正常,但是,当用户按下浏览器的后退/前进按钮时,它就会中断。

这是问题的细分...

我的路由器的设置方式是这样的:

const router = new Router({
  mode: 'history',
  routes: [
    {
      path: '/browse/food/:foodId',
      name: 'Food',
      component: foodComp,
      children: [
        {
          path: ':routeTitle',
          name: 'FoodDescription',
          component: foodDescriptionComp,
          props: {
            viewMode: 'TEXT',
          },
        },
        {
          path: ':routeTitle',
          name: 'FoodImage',
          component: foodDescriptionComp,
          props: {
            viewMode: 'IMAGE',
          },
        },
        {
          path: ':routeTitle/food-translation',
          name: 'FoodTranslation',
          component: foodTranslationComp,
        },
      ],
    },
    ...lotsOfOtherRoutes,
  ],
});

...它内置了分页功能,可以在不同的食物之间切换,即在单击“下一页”(或页码)按钮时触发,其设置如下:foodDescriptionCompmethod

methods: {
  switchPage(pageNumber, foodIdForPageSelection) {
    this.$router.push({
      name: 'FoodDescription',
      params: { foodId: foodIdForPageSelection },
    });

    await this.loadFoodByPage({
      catalogId: this.catalogId,
      pageNumber,
    });
  }
}

如您所见,页面路由器将更新路由,然后作为单独的操作,我调用 .如上所述,这很好用,但是如果用户尝试使用他们的浏览器后退按钮,那么将永远不会调用。loadFoodByPageloadFoodByPage

我尝试了几件事来解决这个问题:

我尝试添加并移动到 ,但是,仅在初始页面加载时点击。beforeEnterloadFoodByPagebeforeEnter

我相信使用会起作用,但是我们的应用程序中有数百条路由,为将在每个页面上检查的特定子路由添加逻辑似乎很愚蠢。beforeEach

我试过变得非常笨拙,但下面的这个解决方案不起作用,因为它只保留了一键的历史记录 - 例如,如果我从登录页面重定向到食物 1,然后转到 2、3、4,然后我按浏览器中的后退按钮,它将返回并加载第 3 页, 然后我再次按回去,它进入登录页面。即使这种方法确实有效,它也感觉非常反模式。

// Back/Forward browser button pressed
window.onpopstate = function () {
  window.location.reload();
};
javascript vue.js vuejs2 vue-router

评论

0赞 paddyfields 11/14/2023
不要使用窗口事件,解决方案应该在“vue”中完成。您需要在 中添加一个观察者,该观察者将监视 route.name 美元,如果 ,则调用foodDescriptionComppage.to === 'myChildRoute'loadFoodByPage

答:

1赞 paddyfields 11/14/2023 #1

未经测试,但这原则上应该有效。这个想法是,这将在每次路由更改时触发,并在 URL 中设置页码。如果 URL 中没有页码,则使用 1。

methods: {
  switchPage(pageNumber, foodIdForPageSelection) {
    this.$router.push({
      name: 'FoodDescription',
      params: { foodId: foodIdForPageSelection },
      query: { pageNumber }
    })
},
watch:{
  '$route' (to, from){
    if(to.name === 'FoodDescription') {
      this.loadFoodByPage({
        catalogId: this.catalogId,
        pageNumber: this.pageNumber ? this.pageNumber : 1,
    });
    }
  }
},
computed: {
  pageNumber: () => {
      return this.$route.query.pageNumber;
    }
  }
}
1赞 Keyboard Corporation 11/14/2023 #2

已经尝试了一些方法来解决这个问题:

我尝试添加 beforeEnter 并将 loadFoodByPage 移动到 beforeEnter,但是,这仅在初始页面加载时被击中。

我相信使用 beforeEach 会起作用,但是我们的应用程序中有数百条路由,为将在每个页面上检查的特定子路由添加逻辑似乎很愚蠢。

是的,您可以使用 or 来实现此目的。beforeEnterbeforeEach

beforeEnter - 您已经尝试过这种方法,发现它仅适用于初始页面加载。这是因为在使用浏览器的后退按钮导航回路由时,不会重新计算。beforeEnter

beforeEach - 在输入每个路由之前执行此操作,包括使用浏览器的后退按钮导航回路由时,但您担心将此防护添加到应用程序中的所有路由的性能影响。

因此,这里一个潜在的解决方案是为您的路线使用动态模块。这样,您可以添加 以仅应用需要它的路由,而不必将其添加到每个路由中。beforeEnter

例;

// Dynamic route module
const foodDescriptionModule = {
 beforeEnter: async (to, from, next) => {
   // Call your API here
   await store.dispatch('loadFoodByPage', {
     catalogId: to.params.catalogId,
     pageNumber: to.params.pageNumber,
   });

   next();
 },
 children: [
   {
     path: ':routeTitle',
     name: 'FoodDescription',
     component: foodDescriptionComp,
     props: {
       viewMode: 'TEXT',
     },
   },
   {
     path: ':routeTitle',
     name: 'FoodImage',
     component: foodDescriptionComp,
     props: {
       viewMode: 'IMAGE',
     },
   },
   {
     path: ':routeTitle/food-translation',
     name: 'FoodTranslation',
     component: foodTranslationComp,
   },
 ],
};

// Main router configuration
const router = new Router({
 mode: 'history',
 routes: [
   {
     path: '/browse/food/:foodId',
     name: 'Food',
     component: foodComp,
     children: [foodDescriptionModule],
   },
   ...lotsOfOtherRoutes,
 ],
});

在这里,防护装置在单独的模块 () 中定义。然后,此模块将包含在主路由配置的数组中。这样,防护仅应用于 中定义的路由,而不适用于应用程序中的每个路由。beforeEnterfoodDescriptionModulechildrenbeforeEnterfoodDescriptionModule

请注意,这假设该操作在您的 vuex 商店中可用。如果不是这种情况,您将需要相应地调整代码。然后记得将 vuex store 导入到文件顶部的 .此解决方案应允许您在命中特定子路由时执行存储操作,无论该路由是通过按钮按下还是浏览器的后退/前进按钮命中。loadFoodByPageimport store from './store'

评论

0赞 maxshuty 11/15/2023
使用此代码时,我收到以下错误"path" is required in a route configuration.
0赞 maxshuty 11/15/2023
模块是否应该有一个空的或..?path