我发生了以下顺序:
主屏幕
加载屏幕
结果屏幕
在主页上,当有人单击按钮时,我将其通过以下方式发送到加载屏幕:
this.$router.push({path: "/loading"});
任务完成后,将通过
发送到结果屏幕 this.$router.push({path: "/results/xxxx"});
问题是,通常他们希望从结果返回主屏幕,但是当他们单击返回时,它们将被发送到再次加载,从而将它们发送回结果,因此它们陷入了无限循环&无法返回主屏幕。
有什么办法解决此问题吗?理想情况下,我希望有一个选项:
this.$router.push({path: "/loading", addToHistory: false});
这会将他们发送到路线而不将其添加到历史记录中。
答案 0 :(得分:8)
有一种处理这种情况的完美方法
您可以使用in-component guard来控制颗粒级别的路线
在代码中进行以下更改
在主屏幕组件中
将此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应用程序来重现相同的问题。根据您的问题,它在我的本地计算机上有效。希望它也能够解决您的问题。
答案 1 :(得分:3)
使用 this.$router.replace
这应该有一个真正的答案:
// On login page
// Use 'push' to go to the loading page.
// This will add the login page to the history stack.
this.$router.push({path: "/loading"});
// Wait for tasks to finish
// Use 'replace' to go to the results page.
// This will not add '/loading' to the history stack.
this.$router.replace({path: "/results/xxxx"});
为了进一步阅读 Vue Router 在幕后使用 History.pushState() 和 History.replaceState()。
答案 2 :(得分: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
为真,或者results
为null,但是您愿意,可以创建一个假数据集以进行迭代并创建骨架版本,如果您愿意的话。否则,您可以按照自己的方式保留事物。
答案 3 :(得分: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 {}
答案 4 :(得分:1)
另一种选择是使用History API。
进入“结果”屏幕后,您可以使用ReplaceState替换浏览器历史记录中的URL。
答案 5 :(得分: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"});
}
}
答案 6 :(得分:0)
您的加载屏幕完全不受vue-router的控制。 最好的选择是在加载屏幕上使用模式/叠加层,由javascript控制。关于vue的例子很多。如果找不到任何东西,那么vue-bootstrap有一些简单的示例可以实现。