vue.js - 在Vue-Router上替换路线时出现导航重复错误

标签 vue.js

我在上面的标题中定义的vue路由器有问题。

假设我有一个router-view,当用户从页面选择器组件中选择一个页面时,它会动态呈现页面。我期望的是我必须获得如下网址:
http://localhost:port/editor/{appSlug}/layout-editor/page/{pageSlug}
但是,相反,我得到了这个:
http://localhost:port/editor/{appSlug}/layout-editor/page/{pageSlug}-randomString
控制台显示此错误:

NavigationDuplicated {_name: "NavigationDuplicated", name: "NavigationDuplicated", message: "Navigating to current location ("/editor/penerimaa…/page/input-pendaftaran-edrpekvl") is not allowed", stack: "Error↵ at new NavigationDuplicated (webpack-int…/node_modules/vue/dist/vue.runtime.esm.js:3876:9)"}`



我已经检查了路由器文件,但仍然找不到我的路由出了什么问题。我还尝试了this question的解决方案,但仍然出现此错误。

有人可以帮我吗?

请看一下我的代码:
router.js
import Vue from 'vue'
import Router from 'vue-router'
import store from './store/index'

import Home from './views/home/Index.vue'

Vue.use(Router)

let router = new Router({
  mode: 'history',
  linkActiveClass: 'active',
  linkExactActiveClass: 'exact-active',
  routes: [{
      path: '/',
      name: 'home',
      component: Home,
      meta: {
        requiresAuth: true
      }
    },
    {
      path: '/login',
      name: 'login',
      // route level code-splitting
      // this generates a separate chunk (login.[hash].js) for this route
      // which is lazy-loaded when the route is visited.
      component: () => import('./views/auth/Login.vue'),
      meta: {
        requiresGuest: true
      }
    },
    {
      path: '/register',
      name: 'register',
      component: () => import('./views/auth/Register.vue'),
      meta: {
        requiresGuest: true
      }
    },
    {
      path: '/forgot-password',
      name: 'forgot-password',
      component: () => import('./views/auth/extras/ForgotPassword.vue'),
      meta: {
        requiresGuest: true
      }
    },
    {
      path: '/database',
      name: 'database',
      component: () => import('./views/database/Index.vue'),
      meta: {
        requiresAuth: true
      }
    },
    {
      path: '/third-parties',
      name: 'third-parties',
      component: () => import('./views/third-parties/Index.vue'),
      meta: {
        requiresAuth: true
      }
    },
    {
      path: '/editor',
      component: () => import('./components/ViewRenderer.vue'),
      meta: {
        requiresAuth: true,
        requiresAdmin: true,
        requiresEditor: true,
      },
      children: [{
        props: true,
        path: ':appSlug/layout-editor',
        name: 'layout-editor',
        component: () => import('./views/editor/Index.vue'),
        children: [{
          props: true,
          path: 'page/:pageSlug',
          name: 'layout-renderer',
          component: () => import('./components/LayoutRenderer.vue'), // this is where the error occured.
        }],
      }]
    },
  ]
})


// Route Middlewares
router.beforeEach((to, from, next) => {
  const isLoggedIn = store.getters['auth/isLoggedIn']

  // Role getters
  const isAdmin = store.getters['auth/isAdmin']
  const isEditor = store.getters['auth/isEditor']

  // Redirect to the login page if the user is not logged in
  // and the route meta record is requires auth
  if (to.matched.some(record => record.meta.requiresAuth) && !isLoggedIn) {
    next('/login')
  }

  // Redirect to the homepage page if the user is logged in
  // and the route meta record is requires guest
  if (to.matched.some(record => record.meta.requiresGuest) && isLoggedIn) {
    next('/')
  }

  // Redirect to the preview page if the user is logged in
  // but has no role assigned or the role is user
  if (to.matched.some(
      record => (
        record.meta.requiresAuth &&
        record.meta.requiresAdmin &&
        record.meta.requiresEditor
      )) && isLoggedIn && isAdmin !== true && isEditor !== true) {

    next('/')

  }

  // Pass any access if not matches any of conditions above
  next()
})

export default router

Editor/Index.vue
<template>
  <div class="layout-editor container-fluid">
    <ActivityBar></ActivityBar>

    <Sidebar title="Layout Editor">
      <PalleteControl></PalleteControl>
      <Pallete :items="components" :list-style="pallete"></Pallete>
    </Sidebar>

    <Navbar class="editor-navbar">
      <PageSelector></PageSelector>
      <BaseButton id="create-page-button" text="Create new page"></BaseButton>
    </Navbar>

    <!-- Every selected page layout rendered here -->
    <ViewRenderer></ViewRenderer>

    <CommitBar></CommitBar>
  </div>
</template>

<script>
  import components from "@/data/components.json";
  import data from "@/data/table.json";
  import { mapGetters } from "vuex";

  export default {
    name: "LayoutEditor",

    data() {
      return {
        components,
        pallete: "grid"
      };
    },
    computed: {
      ...mapGetters({
        current: "apps/current" // Get current app
      })
    },
    mounted() {
      this.listenPalleteEvent();
      this.listenPageSelectorEvent();
    },
    methods: {
      listenPalleteEvent() {
        EventBus.$on("switch-list-style", () => this.switchPallete());
      },
      switchPallete() {
        if (this.pallete == "grid") return (this.pallete = "list");
        return (this.pallete = "grid");
      },
      listenPageSelectorEvent() {
        EventBus.$on("page-selected", component => {
          this.$router.replace({
            name: "layout-renderer",
            params: { pageSlug: component.pageSlug, component }
          });
        });
      }
    }
  };
</script>

<style lang="scss" scoped>
  .layout-editor {
    padding-left: 530px;
  }
</style>

components/PageSelector.vue
<template>
  <BaseDropdown
    id="pages-dropdown-button"
    id-obj="pageId"
    name-obj="pageName"
    :items="filtered"
    :has-item-control="true"
    text="Create new page or choose one from here"
    event-keyword="page-selected"
  >
    <BaseInput
      name="page-filter"
      v-model="filter"
      :borderless="true"
      placeholder="Search by page name..."
    ></BaseInput>

    <template #item-control>
      <div class="item-control">
        <BaseButton id="duplicate-page-button" text="Duplicate"></BaseButton>
        <BaseButton id="delete-page-button" text="Delete"></BaseButton>
      </div>
    </template>
  </BaseDropdown>
</template>

<script>
  import { mapGetters } from "vuex";

  export default {
    data() {
      return {
        filter: ""
      };
    },

    created() {
      // Dispatch fetch page request on vuex store when the instance was created.
      this.$store.dispatch("pages/load", this.currentApp);
    },

    computed: {
      // Map getters from vuex store.
      ...mapGetters({
        pages: "pages/pages",
        currentApp: "apps/current"
      }),

      // Filter pages as long as user type in the dropdown input.
      filtered() {
        return this.pages.filter(page => {
          return page.pageName.toLowerCase().includes(this.filter.toLowerCase());
        });
      }
    }
  };
</script>

<style lang="scss" scoped>
  @import "../../sass/variables";

  ::v-deep .dropdown-item {
    position: relative;
    display: flex;
    justify-content: space-between;
    align-items: center;

    &:hover {
      .item-control {
        opacity: 1;
      }
    }
  }

  ::v-deep .item-control {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    opacity: 0;

    .form-group {
      margin-bottom: 0;
    }

    .form-group .btn {
      border-radius: 30px;
      height: auto;
    }

    .form-group:first-child .btn {
      margin-right: 5px;
    }

    .form-group:last-child .btn {
      background-color: $red;
      border-color: $red;
      color: white;
      &:hover {
        background-color: darken($color: $red, $amount: 3);
      }
    }
  }
</style>

components/ViewRenderer.vue
<template>
  <router-view />
</template>

components/LayoutRenderer.vue
<template>
  <div class="layout-renderer">
    <GridLayout
      :layout.sync="components"
      :col-num="12"
      :row-height="30"
      :is-draggable="true"
      :is-resizable="true"
      :is-mirrored="false"
      :vertical-compact="true"
      :margin="[10, 10]"
      :use-css-transforms="false"
      :responsive="true"
      :auto-size="true"
    >
      <GridItem
        v-for="component in components"
        :key="component.i"
        :x="component.x"
        :y="component.y"
        :w="component.w"
        :h="component.h"
        :i="component.i"
      >
        <ComponentRenderer :component="component" />
      </GridItem>
    </GridLayout>
  </div>
</template>

<script>
  import { mapState } from "vuex";
  import VueGridLayout from "vue-grid-layout";

  export default {
    components: {
      GridLayout: VueGridLayout.GridLayout,
      GridItem: VueGridLayout.GridItem
    },
    data() {
      return {
        components: []
      };
    },
    created() {
      this.fetchComponents();
    },
    methods: {
      /**
       * Fetch component from the backend based on the pageId
       * occured by the vue-router's route parameters.
       *
       * @return void
       */
      fetchComponents() {
        let pageId = this.$route.params.component.pageId;
        this.$store.dispatch("components/fetchComponents", pageId).then(() => {
          this.components = this.$store.getters["components/components"];
        });
      }
    }
  };
</script>

<style lang="scss" scoped>
  .layout-renderer {
    margin-bottom: 100px;
  }

  @media only screen and (max-width: 501px) {
    .vue-grid-item {
      height: fit-content !important;
      transform: none !important;
      position: relative !important;
      margin-bottom: 10px;
    }
  }

  @media (hover: none), (hover: on-demand) {
    .vue-grid-item {
      height: fit-content !important;
      transform: none !important;
      position: relative !important;
      margin-bottom: 10px;
    }
  }
</style>

最佳答案

尽管joyBinary's answer解决了该问题,但它也吞没了所有其他可能不是您期望的行为的错误。

这种方法解决了这个问题:

const originalPush = Router.prototype.push;
Router.prototype.push = function push(location) {
  return originalPush.call(this, location).catch(err => {
    if (err.name !== 'NavigationDuplicated') throw err
  });
}

关于vue.js - 在Vue-Router上替换路线时出现导航重复错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58634914/

相关文章:

django - 让 Django、VUE、CORS 和 CSRF 与真实世界的例子一起工作

javascript - :onchange event causing loop thru method

typescript - 使用 TypeScript 向 Vue 3 添加全局属性

javascript - [Vue警告] :Property or method is not defined on the instance but referenced during render

javascript - 在 vue 路由中匹配查询参数

javascript - Vue 2 在实例中使用外部变量

vue.js - 使用 Vuetify v-btn 和 Vue 路由器在新窗口中打开链接

javascript - 如何在使用 vue-resource 时使用 data(){} 但不使用 Mounted(){} 来启动函数

arrays - How do I preselect a vue-multiselect option when options is an array of objects?

vue.js - 编译报错【VueLoaderPlugin Error】 vue-loader 15 目前不支持带有oneOf的vue规则