Как нажать на vue-router без добавления в историю?

12

У меня следующая последовательность происходит:

  • Главный экран

  • Экран загрузки

  • Экран результатов

На домашней странице, когда кто-то нажимает кнопку, я отправляю их на экран загрузки через:

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

И как только их задача заканчивается, они отправляются на экран результатов через

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

Проблема в том, что обычно они хотят вернуться от результатов к главному экрану, но когда они нажимают назад, они отправляются на загрузку снова, что возвращает их к результатам, поэтому они застряли в бесконечном цикле и не могут перейти вернуться к главному экрану.

Любые идеи, как это исправить? Я бы в идеале хотел бы, если бы была такая опция:

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

который отправит их на маршрут, не добавляя его в историю.

Нажмите Upvote
источник
5
this.$router.replace({path: "/results/xxxx"})
Роланд Старк
@RolandStarke Спасибо - это заставляет работать кнопку «Назад» и вернуться в главное меню, но я теряю кнопку «Вперед» и не могу перейти вперед - есть идеи?
Нажмите Upvote
Процесс такой: Главное меню, Загрузка, Результаты. Я звоню $router.replaceиз загрузки. Теперь это позволяет мне вернуться к результатам -> Main, но я не могу перейти к результатам.
Нажмите Upvote
Другим вариантом будет отсутствие маршрута погрузки. Скорее нажмите непосредственно на маршрут результатов, который выполняет выборку данных при создании, а затем отображает представление загрузки до его завершения. Тогда нет никакой перенаправления, и история и пользовательский поток должны остаться без изменений без $ router.replace.
ArrayKnight
@ClickUpvote, вы нашли какое-либо решение этой проблемы ...
Чан

Ответы:

8

Есть идеальный способ справиться с этой ситуацией

Вы можете использовать встроенную защиту для контроля маршрута на уровне гранул.

Сделайте следующие изменения в вашем коде

В главном компоненте экрана

Добавьте этот beofreRouteLeave guard в настройках компонента, перед тем как перейти к «экрану результатов», вы настраиваете маршрут для прохождения только через экран загрузки.

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

В компоненте экрана загрузки

Если маршрут возвращается от результата к загрузке, то он не должен приземлиться и сразу перейти к главному экрану.

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

На экране загрузки защита beforeRouteEnter НЕ имеет доступа к этому, потому что защита вызывается до подтверждения навигации, поэтому новый входящий компонент еще даже не был создан. Так что, воспользовавшись этим, вы не получите бесконечные вызовы при маршрутизации с экрана результатов.

В компоненте экрана результатов

если вы используете go back, то он не должен попадать в загрузку и сразу переходить на главный экран

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 {}
Estradiaz
источник
Это та реализация, о которой я говорю, хотя я, как правило, внедряю свой загрузочный компонент непосредственно в компоненты моей страницы, а не создаю оболочку для отслеживания маршрута. Причина в том, что мне нравится использовать скелетный подход, при котором компонент контента загружается в частично загруженное состояние, чтобы дать пользователю визуальную обратную связь о том, чего ожидать, а затем наложить загрузочный компонент (если ему нужно заблокировать все действия) или встроить его, если пользователь может использовать пользовательский интерфейс во время загрузки данных. Все дело в восприятии скорости пользователями и разблокировании их способности делать то, что они хотят, как можно быстрее.
ArrayKnight
@ArrayKnight думая абстрактно, можно просто поменять компонент вида маршрутизатора с его базовым компонентом маршрута и получить какую-то оболочку загрузчика - но я согласен, что
загрузка
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, как вы предпочитаете, вы можете создать поддельный набор данных для перебора и создания версий скелета, если хотите. В противном случае вы можете просто оставить все как есть.

ArrayKnight
источник
Экран загрузки, который у меня есть, занимает весь экран, поэтому я не могу придумать, как его отобразить, не имея своего собственного маршрута. Смотрите naminator.io для живой демонстрации. Открыты для любых других идей для этого.
Нажмите Upvote
Утром я посмотрю правильно, но моя первоначальная мысль: нет причины, по которой вы не могли бы использовать фиксированное положение для захвата экрана.
ArrayKnight
Глядя на ваш дизайн, кажется, что есть несколько вариантов: вы можете визуализировать загрузчик вместо содержимого результатов, а затем переключаться после извлечения данных; или вы можете наложить загрузчик на скелет ваших результатов, а затем обновить и заполнить результаты и удалить загрузчик. Учитывая, что загрузчик не покрывает заголовок (оболочку сайта), вам не нужно фиксировать положение.
ArrayKnight
@ClickUpvote, дайте мне знать, что вы думаете об этом подходе.
ArrayKnight
@ ArrayKnight, вопрос в том, что маршруты уже реализованы и работают, как и ожидалось, этот подход к загрузке может не подходить здесь, я проходил через эту же ситуацию. Маршрут загрузки может также иметь запрос отправки формы, такой как платежный шлюз или некоторые действия после отправки формы. В этом сценарии мы не можем предусмотреть удаление маршрута. Но все же на уровне маршрутизатора vue, который мы можем иметь до входа в систему защиты, почему я предложил этот подход, так как экземпляр загрузки компонента vue еще не создан на этом хуке, и его преимущество состоит в том, чтобы решить, следует ли входить в этот маршрут или нет на основе предыдущего маршрута
Чанс
1

Другой вариант - использовать History API .

Как только вы попадаете на экран Результаты, вы можете использовать ReplaceState для замены URL в истории браузера.

Angelo
источник
0

Это можно сделать с помощью beforeEachкрючка роутера.

Что вам нужно сделать, это вы должны сохранить переменную глобально или в localStorage в loadingкомпоненте, когда данные загружаются (до перенаправления на resultsкомпонент):

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();
});

Кроме того, не забудьте сбросить значение false в вашем mainкомпоненте при нажатии кнопки запроса (перед маршрутизацией к loadingкомпоненту):

export default {
  name: "main",
  ...
  queryButtonClicked() {
    localStorage.setItem('DATA_LOADED', JSON.stringify(false));
    this.$router.push({path: "/loading"});
  }
}
Yogesh
источник
Это решение хрупкое (вводит долги по техническому обслуживанию) и требует записи для каждого возможного маршрута, для которого вам может понадобиться эта функциональность.
ArrayKnight
0

Ваш загрузочный экран вообще не должен контролироваться vue-router. Лучший вариант - использовать модальный / оверлей для экрана загрузки, управляемый javascript. Есть много примеров вокруг Vue. Если вы ничего не можете найти, то у vue-bootstrap есть несколько простых примеров для реализации.

omarjebari
источник