Jak wcisnąć do routera Vue bez dodawania do historii?

12

Występuje następująca sekwencja:

  • Główny ekran

  • Ładowanie ekranu

  • Ekran wyników

Gdy na stronie głównej ktoś kliknie przycisk, wysyłam go do ekranu ładowania, poprzez:

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

Po zakończeniu zadania są wysyłane do ekranu wyników za pośrednictwem

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

Problem polega na tym, że zwykle chcą przejść z wyników z powrotem do ekranu głównego, ale gdy klikną z powrotem, są wysyłani do ładowania ponownie, co przesyła je z powrotem do wyników, więc utknęli w nieskończonej pętli i nie mogą wrócić powrót do ekranu głównego.

Wszelkie pomysły, jak to naprawić? Idealnie chciałbym, gdyby istniała opcja:

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

który wysłałby ich na trasę bez dodawania jej do historii.

Kliknij opcję Upvote
źródło
5
this.$router.replace({path: "/results/xxxx"})
Roland Starke
@RolandStarke Dzięki - dzięki temu przycisk Wstecz działa i wraca do menu głównego, ale zgubiłem przycisk Dalej i nie mogę przejść dalej - jakieś pomysły?
Kliknij Upvote
Proces jest następujący: Menu główne, Ładowanie, Wyniki. Dzwonię $router.replacez Ładowanie. To pozwala mi teraz wrócić do wyników -> Główne, ale nie mogę przejść do wyników.
Kliknij Upvote
Inną opcją byłoby brak trasy ładowania. Zamiast tego wypychaj bezpośrednio do trasy wyników, którą pobiera dane przy tworzeniu, a następnie renderuje widok ładowania, aż zostanie ukończony. Wtedy nie ma przekierowywania i historii, a przepływ użytkowników powinien pozostać nienaruszony bez $ router.replace.
ArrayKnight
@ClickUpvote znalazłeś jakieś rozwiązanie tego problemu ...
Chans

Odpowiedzi:

8

Istnieje doskonały sposób na poradzenie sobie z tą sytuacją

Za pomocą wbudowanego zabezpieczenia można kontrolować trasę na poziomie granulatu

Wprowadź następujące zmiany w kodzie

W głównym ekranie

Dodaj tę osłonę beofreRouteLeave w opcjach komponentu, przed wyjściem do „ekranu wyników” ustawiasz trasę, aby przejść tylko przez ekran ładowania

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

W trakcie ładowania komponentu ekranu

Jeśli trasa cofa się od wyniku do ładowania, nie powinna tu wylądować i przejść bezpośrednio do ekranu głównego

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

Na ekranie ładowania osłona beforeRouteEnter NIE ma do tego dostępu, ponieważ osłona jest wywoływana przed potwierdzeniem nawigacji, dlatego nowy komponent wchodzący nie został jeszcze utworzony. Korzystając z tego, nie dostaniesz nieskończonych połączeń podczas routingu z ekranu wyników

W wyniku składnik ekranu

jeśli użyjesz Cofnij, to nie powinno wylądować podczas ładowania i przejść bezpośrednio do ekranu głównego

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

Właśnie stworzyłem małą aplikację vue, aby odtworzyć ten sam problem. Działa w moim lokalnym, jak na twoje pytanie. Mam nadzieję, że rozwiąże to również Twój problem .

chans
źródło
2
To rozwiązanie jest kruche (wprowadza dług techniczny) i wymaga wpisów dla każdej możliwej trasy, dla której może być potrzebna ta funkcja.
ArrayKnight,
Całkowicie się zgadzam, wcale nie podoba mi się to rozwiązanie
omarjebari
2

Wydaje mi się, że router.replacejest to właściwa droga - ale wciąż kilka myśli (niesprawdzonych):


Zasadniczo po $routerzmianie renderuje ładujący komponent do momentu jego emisji load:stop, a następnie renderujerouter-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
źródło
Jest to rodzaj implementacji, o której mówię, chociaż zwykle implementuję mój komponent ładujący bezpośrednio w komponentach strony, zamiast tworzyć opakowanie do oglądania trasy. Powodem jest to: lubię stosować podejście szkieletowe, w którym komponent treści ładuje się do stanu częściowo załadowanego, aby dać użytkownikowi wizualną informację zwrotną na temat tego, czego się spodziewać, a następnie nałożyć komponent ładujący (jeśli musi zablokować wszystkie działania) lub wstawić go, jeśli użytkownik może korzystać z interfejsu użytkownika podczas ładowania danych. Chodzi o postrzeganie prędkości przez użytkownika i odblokowywanie jego zdolności do robienia tego, co chcą, tak szybko, jak to możliwe.
ArrayKnight,
@ArrayKnight myślenie abstrakcyjne można po prostu wymienić komponent widoku routera na odpowiadający mu komponent trasy i uzyskać pewnego rodzaju opakowanie modułu ładującego - ale zgadzam się, że umieszczenie modułu ładującego na trasie wydaje się być kiepskim projektem
Estradiaz
2

To trudne połączenie, biorąc pod uwagę, jak mało wiemy o tym, co dzieje się na trasie ładowania.

Ale...

Nigdy nie musiałem budować trasy ładowania, tylko ładowanie komponentów, które są renderowane na wielu trasach podczas etapu inicjowania / gromadzenia danych.

Jednym z argumentów za brakiem trasy ładowania byłoby to, że użytkownik mógłby potencjalnie przejść bezpośrednio do tego adresu URL (przypadkowo), a następnie wydaje się, że nie miałby wystarczającego kontekstu, aby wiedzieć, gdzie wysłać użytkownika lub jakie działania podjąć. Chociaż może to oznaczać, że w tym momencie popada w trasę błędu. Ogólnie rzecz biorąc, nie jest to świetne doświadczenie.

Innym jest to, że jeśli uprościsz swoje trasy, nawigacja między trasami stanie się znacznie prostsza i będzie działać zgodnie z oczekiwaniami / pożądanymi bez użycia $router.replace.

Rozumiem, że to nie rozwiązuje pytania w sposób, w jaki pytasz. Ale proponuję przemyśleć tę trasę ładowania.

App

<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')

Muszla

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

Strona główna

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

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

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

Strona wyników

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

Składnik wyników

Zasadniczo dokładnie ten sam składnik wyników, który już masz, ale jeśli loadingjest to prawda lub jeśli resultsma wartość null, jakkolwiek wolisz, możesz utworzyć fałszywy zestaw danych, aby iterować i tworzyć wersje szkieletowe, jeśli chcesz. W przeciwnym razie możesz zachować wszystko tak, jak chcesz.

ArrayKnight
źródło
Ekran ładowania, który mam, zajmuje cały ekran, więc nie mogę wymyślić sposobu, aby go wyświetlić bez własnej trasy. Zobacz naminator.io, aby zobaczyć demo na żywo. Otwarty na wszelkie inne pomysły.
Kliknij Upvote,
Nadaję mu odpowiedni wygląd rano, ale początkowo pomyślałem, że nie ma powodu, dla którego nie mógłbyś użyć pozycji ustalonej do przejęcia ekranu.
ArrayKnight
Patrząc na twój projekt, wydaje się, że istnieje kilka opcji: możesz renderować moduł ładujący zamiast zawartości wyników, a następnie przełączać się po pobraniu danych; lub możesz nałożyć moduł ładujący szkielet wyników, a następnie zaktualizować i wypełnić wyniki oraz usunąć moduł ładujący. Biorąc pod uwagę, że moduł ładujący nie obejmuje nagłówka (powłoki witryny), nie powinno być potrzeby ustalania pozycji.
ArrayKnight
@ClickUpvote daj mi znać, co sądzisz o tym podejściu.
ArrayKnight
@ ArrayKnight, pytanie brzmi: trasy są już zaimplementowane i działa zgodnie z oczekiwaniami, to podejście ładowania może się tu nie zmieścić, przeżyłem tę samą sytuację. Trasa ładowania może również zawierać żądanie wysłania formularza, takie jak bramka płatności lub niektóre czynności wysyłania formularza. W tym scenariuszu nie możemy polegać na usunięciu trasy. Ale wciąż na poziomie routera vue, który możemy mieć przed wejściem na osłony, dlaczego zasugerowałem to podejście, instancja vue komponentu ładującego nie jest jeszcze utworzona w tym haku, a zaletą jest zdecydowanie, czy wejść na tę trasę, czy nie w oparciu o poprzednią trasę
chans
1

Inną opcją jest użycie History API .

Po przejściu do ekranu wyników możesz użyć narzędzia ReplaceState do zastąpienia adresu URL w historii przeglądarki.

Angelo
źródło
0

Można to zrobić za pomocą beforeEachhaka routera.

To, co musisz zrobić, to zapisać zmienną globalnie lub w localStorage w loadingkomponencie, gdy dane są ładowane (przed przekierowaniem do resultskomponentu):

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

A następnie powinieneś sprawdzić tę zmienną w zaczepie BeforeEach i przejść do właściwego komponentu:

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

Pamiętaj również, aby po mainkliknięciu przycisku zapytania (przed routingiem do loadingkomponentu) zresetować wartość na wartość false w komponencie:

export default {
  name: "main",
  ...
  queryButtonClicked() {
    localStorage.setItem('DATA_LOADED', JSON.stringify(false));
    this.$router.push({path: "/loading"});
  }
}
Jogesh
źródło
To rozwiązanie jest kruche (wprowadza dług techniczny) i wymaga wpisów dla każdej możliwej trasy, dla której może być potrzebna ta funkcja.
ArrayKnight,
0

Ekran ładowania nie powinien być w ogóle kontrolowany przez router vue. Najlepszą opcją jest użycie modalnej / nakładki na ekran ładowania, kontrolowanej przez javascript. Istnieje wiele przykładów vue. Jeśli nie możesz nic znaleźć, vue-bootstrap ma kilka łatwych przykładów do zaimplementowania.

omarjebari
źródło