如何在不添加历史的情况下推送到vue-router?


12

我发生了以下顺序:

  • 主屏幕

  • 载入画面

  • 结果画面

在主页上,当有人单击按钮时,我将其通过以下方式发送到加载屏幕:

this.$router.push({path: "/loading"});

任务完成后,将通过以下方式将其发送到结果屏幕

this.$router.push({path: "/results/xxxx"});

问题是,通常他们希望从结果返回主屏幕,但是当他们单击返回时,它们将被发送到再次加载,从而将它们发送回结果,因此它们陷入了无限循环并且无法前进返回主屏幕。

任何想法如何解决这一问题?理想情况下,我希望有一个类似的选择:

this.$router.push({path: "/loading", addToHistory: false});

这会将他们发送到该路线而不将其添加到历史记录中。


5
this.$router.replace({path: "/results/xxxx"})
罗兰·史塔克

@RolandStarke谢谢-这样可以使后退按钮正常工作并返回主菜单,但是我丢失了前进按钮并且无法再次前进-有什么想法吗?
点击Upvote '19

过程是:主菜单,加载,结果。我正在$router.replace从载入中打电话。现在,这使我从“结果->主菜单”中返回,但随后无法继续显示结果。
点击Upvote '19

另一个选择是没有装载路线。而是直接推送到结果路由,该路由将在创建时提取数据,然后呈现一个加载视图,直到完成为止。这样就没有重新路由,没有$ router.replace,历史记录和用户流也将保持不变。
ArrayKnight,

@ClickUpvote您是否找到此问题的任何解决方案...
chans

Answers:


8

有一个完美的方法来处理这种情况

您可以使用组件内防护来控制颗粒级别的路线

在代码中进行以下更改

在主屏幕组件中

将这个beofreRoute留在组件选项中,在进入“结果屏幕”之前,将路由设置为仅通过加载屏幕

beforeRouteLeave(to, from, next) {
   if (to.path == "/result") {
      next('/loading')
    }
    next();
  }, 

正在加载屏幕组件

如果路线从结果返回到加载,则它不应降落在此处并直接跳转到主屏幕

beforeRouteEnter(to, from, next) {
    if (from.path == "/result") {
      next('/main')
    }
     next();
  },

在加载屏幕中,beforeRouteEnter防护措施对此无权访问,因为在确认导航之前调用了该防护措施,因此甚至尚未创建新的输入组件。因此,利用此优势,从结果屏幕进行路由时,您将不会触发无限呼叫

结果屏幕组件

如果您使用返回,则它不应降落在加载中,而直接跳转到主屏幕

beforeRouteLeave(to, from, next) {
    if (to.path == "/loading") {
      next('/')
    }
    next();
  },

我刚刚创建了一个小型Vue应用程序来重现相同的问题。根据您的问题,它在我的本地计算机上有效。希望它也能解决您的问题


2
该解决方案非常脆弱(引入了维护技术债务),并且需要您可能需要此功能的所有可能路线的条目。
ArrayKnight,

完全同意,我根本不喜欢这种解决方案
omarjebari

2

我想这router.replace是要走的路-但仍然有些思路(未经试验):


基本上在$router更改时,它会渲染加载组件,直到它发出load:stop,然后才渲染router-view


import { Vue, Component, Watch, Prop } from "vue-property-decorator";

@Component<RouteLoader>({
    render(h){
        const rv = (this.$slots.default || [])
        .find(
            child => child.componentOptions
            //@ts-ignore 
            && child.componentOptions.Ctor.extendedOptions.name === "RouterView"
        )
        if(rv === undefined) 
            throw new Error("RouterView is missing - add <router-view> to default slot")

        const loader = (this.$slots.default || [])
        .find(
            child => child.componentOptions
            //@ts-ignore 
            && child.componentOptions.Ctor.extendedOptions.name === this.loader
        )
        if(loader === undefined) 
            throw new Error("LoaderView is missing - add <loader-view> to default slot")
        const _vm = this 
        const loaderNode = loader.componentOptions && h(
            loader.componentOptions.Ctor,
            {
                on: {
                    // "load:start": () => this.loading = true,
                    "load:stop": () => _vm.loading = false
                },
                props: loader.componentOptions.propsData,
                //@ts-ignore
                attrs: loader.data.attrs
            }
        )
        return this.loading && loaderNode || rv
    }
})
export default class RouteLoader extends Vue {
    loading: boolean = false
    @Prop({default: "LoaderView"}) readonly loader!: string
    @Watch("$route")
    loads(nRoute: string, oRoute: string){
        this.loading = true
    }
}

@Component<Loader>({
    name: "LoaderView",
    async mounted(){

        await console.log("async call")
        this.$emit("load:stop")
        // this.$destroy()
    }
})
export class Loader extends Vue {}

我正在谈论的是这种实现,尽管我倾向于将加载组件直接实现到页面组件中,而不是创建路由监视包装器。原因是:我喜欢使用骨架方法,其中内容组件加载到半加载状态,以向用户提供期望的视觉反馈,然后覆盖加载组件(如果需要阻止所有操作),或者在需要时内联用户可以在加载数据时使用UI。一切都与用户对速度的感知以及阻止他们尽快完成自己想要的功能有关。
ArrayKnight

@ArrayKnight认为抽象的人可以简单地将路由器视图组件与其核心响应的路由组件交换,并获得某种装载程序包装器-但我同意将装载机放入路由感觉就像是糟糕的设计
Estradiaz

2

考虑到我们对您的装载路线中发生的事情知之甚少,这是一个艰难的决定。

但...

我从来不需要构建加载路径,只需要在初始化/数据收集阶段在多条路径上呈现的加载组件即可。

关于没有加载路径的一个论点是,用户可能会直接(偶然地)直接导航到该URL,然后似乎没有足够的上下文来知道将用户发送到何处或采取什么措施。尽管这可能意味着它此时会陷入错误路线。总的来说,不是一个很好的经验。

另一个问题是,如果您简化了路线,则路线之间的导航会变得更加简单,并且无需使用即可实现预期/期望的行为 $router.replace

我了解这并不能解决您的问题。但我建议您重新考虑此加载路线。

应用程式

<shell>
    <router-view></router-view>
</shell>

const routes = [
  { path: '/', component: Main },
  { path: '/results', component: Results }
]

const router = new VueRouter({
  routes,
})

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

贝壳

<div>
    <header>
        <nav>...</nav>
    </header>
    <main>
        <slot></slot>
    </main>
</div>

主页

<section>
    <form>...</form>
</section>

{
    methods: {
        onSubmit() {
            // ...

            this.$router.push('/results')
        }
    }
}

结果页

<section>
    <error v-if="error" :error="error" />
    <results v-if="!error" :loading="loading" :results="results" />
    <loading v-if="loading" :percentage="loadingPercentage" />
</section>

{
    components: {
        error: Error,
        results: Results,
    },
    data() {
        return {
            loading: false,
            error: null,
            results: null,
        }
    },
    created () {
        this.fetchData()
    },
    watch: {
        '$route': 'fetchData'
    },
    methods: {
        fetchData () {
            this.error = this.results = null
            this.loading = true

            getResults((err, results) => {
                this.loading = false

                if (err) {
                    this.error = err.toString()
                } else {
                    this.results = results
                }
            })
        }
    }
}

结果部分

基本上与您已经拥有的结果组件完全相同,但是如果loading为true,或者results为null(但是您愿意),则可以根据需要创建一个伪造的数据集以进行迭代并创建框架版本。否则,您可以按照自己的方式保留事物。


我所拥有的加载屏幕占据了整个屏幕,因此如果没有自己的路线,我想不出一种显示它的方法。有关实时演示,请参见naminator.io。对此开放任何其他想法。
点击Upvote '19

早上我会给它一个适当的外观,但我最初的想法是没有理由您不能使用固定位置来接管屏幕。
ArrayKnight,

查看您的设计,似乎有两个选择:可以渲染加载器以代替结果内容,然后在获取数据后进行切换;或者,您可以覆盖结果框架的加载程序,然后更新并填充结果并删除加载程序。考虑到加载程序不会覆盖标题(站点外壳),因此不需要固定位置。
ArrayKnight

@ClickUpvote让我知道您对这种方法的感觉。
ArrayKnight,

@ArrayKnight,问题是路由已经实现并且可以按预期工作,这种加载方法可能不适用于此处,我已经经历了同样的情况。加载路线还可以具有表单发布请求,例如付款网关或某些表单发布操作。在这种情况下,我们不能包括删除路由。但是在vue路由器级别上,我们仍然可以在进入警卫之前拥有该权限,为什么我建议采用这种方法是,尚未在此钩子上创建加载组件的vue实例,其优点是可以根据先前的路由来决定是否进入此路由
chans 19-10-25


0

这可以通过beforeEach路由器的挂钩完成。

您需要做的是,loading加载数据时(在重定向到results组件之前),必须在组件中全局或在组件的localStorage中保存一个变量:

export default {
  name: "results",
  ...
  importantTask() {
    // do the important work...
    localStorage.setItem('DATA_LOADED', JSON.stringify(true));
    this.$router.push({path: "/results/xxxx"});
  }
}

然后,您应该在beforeEach挂钩中检查此变量,然后跳到正确的组件:

// router.js...
router.beforeEach((to, from, next) => {
  const dataLoaded = JSON.parse(localStorage.getItem('DATA_LOADED'));
  if (to.name === "loading" && dataLoaded)
  {
    if (from.name === "results")
    {
      next({ name: "main"} );
    }
    if (from.name === "main")
    {
      next({ name: "results"} );
    }
  }
  next();
});

另外,请记住在main单击查询按钮时(在路由到loading组件之前),将组件中的值重置为false :

export default {
  name: "main",
  ...
  queryButtonClicked() {
    localStorage.setItem('DATA_LOADED', JSON.stringify(false));
    this.$router.push({path: "/loading"});
  }
}

该解决方案非常脆弱(引入了维护技术债务),并且需要您可能需要此功能的所有可能路线的条目。
ArrayKnight

0

您的加载屏幕完全不受vue-router的控制。最好的选择是对加载屏幕使用模式/叠加,由javascript控制。关于vue的例子很多。如果找不到任何内容,那么vue-bootstrap有一些易于实现的示例。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.